diff --git a/.gitignore b/.gitignore index a56a938..33ba5fe 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ .godot/ /android/ /builds/ + +# Blender backup files +*.blend1 diff --git a/ASSET-LICENSE.md b/ASSET-LICENSE.md new file mode 100644 index 0000000..50933b5 --- /dev/null +++ b/ASSET-LICENSE.md @@ -0,0 +1,135 @@ +# NETfishing Asset License 1.0 + +Copyright © 2026 Woofmeow. All rights reserved except for the permissions +expressly granted below. + +## 1. Scope + +This license applies to original NETfishing artwork, graphics, textures, +models, animations, music, sound recordings, narrative content, visual +designs, characters, and other creative assets owned by the NETfishing owners, +publishing as Woofmeow (the **“NETfishing Assets”**). + +It does not apply to: + +- software source code identified as GPL-3.0-or-later; +- third-party material identified in the source repository's + `docs/ATTRIBUTION.md` or the packaged `ATTRIBUTION.md` notice; +- material accompanied by a different license; or +- the NETfishing and Woofmeow names and logos, which are addressed in + `TRADEMARKS.md`. + +Repository location alone does not change third-party ownership or licensing. + +## 2. Definitions + +**Official NETfishing Software** means: + +- an unmodified binary release published by the NETfishing owners through an + official NETfishing distribution channel; or +- a build made from an unmodified official NETfishing source release or tagged + revision. + +**Mod** means a non-standalone add-on created to operate with Official +NETfishing Software. A Mod may add, replace, or alter content loaded by the +Official NETfishing Software, but it may not redistribute a replacement copy +of the game or represent a fork, clone, sequel, or separate game. + +## 3. Permission to use the official game + +You may reproduce NETfishing Assets as technically necessary to install, run, +back up, and privately modify a lawfully obtained copy of Official NETfishing +Software. + +You may capture, publish, and monetize screenshots, gameplay videos, +livestreams, reviews, tutorials, and similar commentary showing a lawfully +obtained copy of NETfishing. This permission does not authorize redistribution +of the source asset files or imply endorsement. + +## 4. Community Mod license + +Subject to this license, you may create, modify, and distribute NETfishing +Assets solely as part of a Mod for Official NETfishing Software. + +This permission is worldwide, nonexclusive, royalty-free, and available for +as long as you comply with this license. It permits only the copies of +NETfishing Assets reasonably necessary for the Mod to function. It does not +permit distributing the original game, a substantially complete collection of +its assets, or a general-purpose asset pack. + +A distributed Mod must: + +1. require a separately obtained copy of Official NETfishing Software; +2. be presented as unofficial community content; +3. include the following notice in its documentation or distribution page: + + > Contains or adapts NETfishing assets © 2026 Woofmeow, used under the + > NETfishing Asset License 1.0. This Mod is unofficial and is not endorsed + > by Woofmeow. + +4. identify material created by the Mod author separately from NETfishing + Assets; and +5. comply with `TRADEMARKS.md`. + +Mods may be shared without charge and may accept voluntary donations that do +not purchase access, features, or preferential treatment. Selling a Mod, +charging for access to it, licensing it commercially, or using NETfishing +Assets in paid products requires separate written permission from both +NETfishing owners. + +## 5. Uses not permitted + +Except with separate written permission from both NETfishing owners, you may +not use or distribute NETfishing Assets: + +- in a fork, clone, remake, sequel, or standalone game; +- with a modified distribution of the GPL-covered NETfishing code that is not + Official NETfishing Software; +- in another game, application, asset library, template, or merchandise; +- as a substantially complete asset extraction or archive; +- in a way that implies sponsorship, endorsement, or official status; +- to register a confusingly similar trademark, domain, account, or product + identity; or +- under terms that claim ownership of the NETfishing Assets themselves. + +The GPL permits forks and redistribution of the GPL-covered code. This asset +license does not permit those forks to carry NETfishing Assets. A code fork +must remove or replace the NETfishing Assets and reserved branding unless both +owners grant separate written permission. + +## 6. Ownership of Mods + +You retain ownership of your original Mod material. The NETfishing owners +retain ownership of the NETfishing Assets and adaptations of those assets to +the extent provided by applicable law. + +This license does not require you to assign your original Mod material to the +NETfishing owners. It also does not grant you ownership of the underlying +NETfishing Assets. + +## 7. Termination and cure + +Your permissions under this license terminate automatically if you violate +its terms. They are reinstated provisionally when you stop the violation and +permanently if both NETfishing owners do not notify you of the violation +within sixty days after it has been cured. + +Termination does not limit remedies available for activity outside this +license. + +## 8. No warranty + +THE NETFISHING ASSETS ARE PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, OR NON-INFRINGEMENT. TO THE MAXIMUM EXTENT +PERMITTED BY LAW, THE OWNERS WILL NOT BE LIABLE FOR ANY CLAIM, DAMAGES, OR +OTHER LIABILITY ARISING FROM THIS LICENSE OR USE OF THE NETFISHING ASSETS. + +## 9. Additional permission + +Uses not granted here require written permission from both NETfishing owners. +A permission granted to one person or project does not amend this license for +others. + +This is a custom asset license, not an open-source software license or a +Creative Commons license. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..62c7813 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing + +NETfishing is currently developed through focused changes on the repository's +active development branch. Coordinate scope with the project owner before +starting substantial work. + +By intentionally submitting material for inclusion, contributors agree to the +applicable grants and representations in +[`CONTRIBUTOR-TERMS.md`](CONTRIBUTOR-TERMS.md). Maintainers must preserve an +affirmative record of that agreement. Contact the owners before submitting if +separate written terms are needed. + +## Change discipline + +- Keep a change focused; avoid unrelated cleanup. +- Inspect the working tree before editing and preserve existing work. +- Do not change protocol, save, settings, identity, or portable-data versions + as release-label housekeeping. +- Treat the host as authoritative for catches, purchases, sales, jobs, mail, + and other shared state. +- Keep presentation-only systems out of save files and network messages. +- Store runtime assets under repository-owned paths; never reference a + workstation sync or temporary directory. +- Record the source and license of newly imported assets in + [`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md). +- Do not assume the GPL code license applies to project assets; observe + [`ASSET-LICENSE.md`](ASSET-LICENSE.md) and third-party terms. +- Do not commit `.godot/`, test data, logs, captures, or build outputs. + +## Before requesting review + +```sh +git diff --check +scripts/run_validations.sh quick +git status --short +``` + +Choose additional focused tests from the validation section of +[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md#validation). +Changes involving networking should run the network suite and a real +two-process check. Visual changes still require graphical review; headless +tests are supporting evidence, not a replacement. + +## Commits + +- Stage audited paths explicitly. +- Use a concise imperative subject that describes the outcome. +- Do not mix generated build artifacts with source changes. +- Do not rewrite published release tags. +- Describe behavior, compatibility impact, validation, and asset provenance in + the review or release record. + +## Style + +Follow the conventions already present in the surrounding GDScript, scene, and +resource files. Prefer typed values, named constants, shared resources, and +small domain-specific services. Avoid duplicating protocol definitions or +using display strings as persistent identifiers. + +New standalone source files should use `SPDX-License-Identifier: +GPL-3.0-or-later` where the file format permits comments. Do not add that +identifier to asset files governed by the NETfishing Asset License. diff --git a/CONTRIBUTOR-TERMS.md b/CONTRIBUTOR-TERMS.md new file mode 100644 index 0000000..8ef207e --- /dev/null +++ b/CONTRIBUTOR-TERMS.md @@ -0,0 +1,94 @@ +# NETfishing contributor terms + +These terms apply to contributions intentionally submitted for inclusion in +NETfishing after these terms are published. They do not retroactively change +ownership of earlier work or replace a separately signed agreement. + +“Owners” means the two NETfishing co-owners who publish the Project as +Woofmeow and are publicly credited as Voyager and Endeavour. “Project” means +the official NETfishing game, its servers, tools, documentation, websites, +ports, updates, and promotional materials. + +## 1. Your ownership and authority + +You retain copyright in your original contribution. By submitting it, you +represent that: + +- you created it or otherwise have authority to grant these permissions; +- its submission does not knowingly violate another party’s copyright, + patent, trademark, privacy, contract, or other rights; +- you have disclosed relevant third-party material and license terms; and +- if an employer or another organization controls the contribution, you have + obtained its permission before submitting. + +Do not submit confidential information, unlawfully obtained material, or +content whose origin and permitted use cannot be documented. + +## 2. Code contributions + +You license an accepted software-code contribution under the GNU General +Public License, version 3 or, at the recipient’s option, any later version +(GPL-3.0-or-later). + +You also grant each recipient a perpetual, worldwide, nonexclusive, +royalty-free patent license under patent claims you can license that are +necessarily infringed by your contribution alone or by its combination with +the Project as submitted. This patent license does not cover later +combinations that are unrelated to your submitted contribution. + +## 3. Asset, writing, and documentation contributions + +For an accepted contribution consisting of artwork, models, animation, audio, +music, writing, level content, branding material, or other non-code creative +work, you grant the Owners a perpetual, worldwide, nonexclusive, +royalty-free, transferable, and sublicensable license to: + +- use, reproduce, modify, adapt, translate, and create derivative works; +- distribute, publicly display, publicly perform, transmit, and make + available the contribution; +- include it in free and commercial Project releases, ports, updates, + merchandise, marketing, and archival materials; and +- license it to Project users under `ASSET-LICENSE.md` or a later Project asset + license jointly approved by the Owners. + +This grant is limited to NETfishing, its promotion, and official successor or +derived NETfishing products. It does not transfer your copyright or authorize +the Owners to sell your contribution as an unrelated standalone asset. + +## 4. Documentation accompanying code + +Documentation submitted as an ordinary part of a code contribution may be +distributed under GPL-3.0-or-later with that code. Project credits and +provenance records may be reproduced in any medium needed to document the +Project and its releases. + +## 5. Attribution + +Provide the name or pseudonym you want used for credit. The Owners will make a +reasonable effort to preserve material attribution, subject to formatting, +privacy, platform, and practical limitations. + +Attribution does not imply that a contributor endorses every Project change +or release. + +## 6. No automatic ownership, employment, or payment + +Submitting a contribution does not provide ownership of NETfishing, create an +employment, agency, partnership, or fiduciary relationship, or create a right +to compensation. Any payment, ownership, or employment arrangement requires a +separate written agreement signed by the appropriate parties. + +## 7. Acceptance + +A contribution is not accepted merely because it was posted, uploaded, or +discussed. The Owners or their authorized maintainers may accept, modify, +decline, or later remove it. + +By intentionally submitting a patch, pull request, asset, or other material +for Project inclusion after receiving notice of these terms, you agree to the +applicable grants above. Maintainers should record the contributor’s +affirmative agreement in the pull request, issue, email, or another durable +written record. + +If you cannot agree to these terms, do not submit the contribution; contact +the Owners to discuss a separate written arrangement. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f6cdd22 --- /dev/null +++ b/LICENSE @@ -0,0 +1,232 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3d3e3f --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# NETfishing + +NETfishing is a multiplayer-first fishing game built with Godot 4.7. The +current pre-alpha includes host-authoritative fishing and economy flows, +freshwater and saltwater habitats, persistent player data, social features, +customization, jobs, and a controller-aware interface. + +The project is under active development. Interfaces, balance, content, and +save migrations may continue to change before a stable release. + +NETfishing is developed and published by **Woofmeow**, the independent team of +co-owners publicly credited as Voyager and Endeavour. + +## Requirements + +- Godot 4.7.x with the GL Compatibility renderer +- Linux, Windows, or an Android development environment supported by Godot +- Bash for the repository helper scripts + +Open `project.godot` in Godot, or start the editor from the repository root: + +```sh +godot --editor --path . +``` + +Run the game directly with: + +```sh +godot --path . +``` + +## Validation + +The validation scripts isolate Godot's settings and application-data roots. +Run the fast, deterministic suite before submitting a change: + +```sh +scripts/run_validations.sh quick +``` + +Additional suites and individual commands are documented in the +[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md#validation) validation section. + +## Building + +Export presets are maintained for Linux, Windows, and Android. Repository +scripts expect the matching Godot export templates and platform toolchains to +already be installed: + +```sh +scripts/build_playtest.sh +scripts/build_android_debug.sh +``` + +Build outputs belong under the ignored `builds/` directory. Release builds +must follow the project's separate tagged-release checklist; these convenience +scripts do not create or publish tags. + +## Project map + +- `main/`: composition root and application orchestration +- `world/`: map, environment, water, and world presentation +- `player/`, `fishing/`, `fish/`: player and fishing domain +- `network/`: transport, identity, and host-authoritative services +- `inventory/`, `items/`, `economy/`, `progression/`, `jobs/`: game state +- `save/`, `settings/`: versioned persistence +- `ui/`: gameplay and menu presentation +- `tests/`: focused Godot validation scripts +- `scripts/`: build, bake, and validation entry points + +All maintained project guidance starts at the +[`docs/` documentation index](docs/README.md). Architecture, content policy, +world and UI authoring, and testing are consolidated in +[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md). + +## Licensing + +- Project-owned software code is licensed under + [GPL-3.0-or-later](LICENSE). +- Project-owned artwork, models, music, and other creative assets are covered + by the [NETfishing Asset License](ASSET-LICENSE.md). It permits community + Mods for official NETfishing software, but not use in forks, clones, or + unrelated products. +- The NETfishing and Woofmeow names and branding are reserved as described in + [TRADEMARKS.md](TRADEMARKS.md). +- Third-party materials retain their own terms; see the consolidated + [credits, notices, and provenance record](docs/ATTRIBUTION.md). +- Contributions are accepted under [CONTRIBUTOR-TERMS.md](CONTRIBUTOR-TERMS.md). + +Material boundaries and binary-release requirements are recorded in that same +[`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md) reference. + +Copyright © 2026 Woofmeow. diff --git a/TRADEMARKS.md b/TRADEMARKS.md new file mode 100644 index 0000000..6848f47 --- /dev/null +++ b/TRADEMARKS.md @@ -0,0 +1,42 @@ +# NETfishing and Woofmeow branding + +The NETfishing and Woofmeow names, logos, icons, trade dress, and other source +identifiers are reserved by the NETfishing owners, publishing as Woofmeow. +Their presence in a GPL-licensed repository does not grant trademark rights or +imply permission to represent another product as official. + +No trademark registration is asserted by this notice. Applicable rights may +exist through use and other law. + +## Permitted references + +You may use the names NETfishing and Woofmeow truthfully to: + +- refer to the official game or development team; +- state that software interoperates with or is a Mod for NETfishing; +- link to an official repository or distribution page; and +- discuss, review, criticize, or report on the Project. + +Use only the amount of branding reasonably necessary to identify the Project. +Do not imply sponsorship, certification, endorsement, or official status. + +An unofficial Mod may use wording such as “Example Mod for NETfishing” if its +name and presentation remain clearly distinct and it displays an unofficial +notice. + +## Forks and redistributed code + +The GNU GPL permits forks and redistribution of covered code. A fork or +modified distribution must: + +- use a distinct product name; +- remove NETfishing and Woofmeow logos, icons, and official trade dress; +- avoid confusingly similar names, domains, package identifiers, and account + names; and +- clearly identify itself as independent and unofficial. + +These branding terms do not limit rights granted by the GPL to the software +code or lawful nominative, descriptive, comparative, or fair use. + +Permission for broader branding use must be obtained in writing from both +NETfishing owners. diff --git a/addons/netfishing_shoreline_baker/plugin.cfg b/addons/netfishing_shoreline_baker/plugin.cfg new file mode 100644 index 0000000..c8c4d06 --- /dev/null +++ b/addons/netfishing_shoreline_baker/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="NETfishing Shoreline Auto-Baker" +description="Rebuilds configured static shoreline ribbons after authored terrain reimports." +author="woofmeow" +version="1.0.0" +script="shoreline_auto_baker.gd" diff --git a/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd b/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd new file mode 100644 index 0000000..18da4af --- /dev/null +++ b/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd @@ -0,0 +1,66 @@ +@tool +extends EditorPlugin + +const TERRAIN_RESOURCE := ( + "res://art/exported/environment/terrain/starter_island.glb" +) +const REGION_SCENE := "res://world/regions/starter_island_region.tscn" + +var _bake_pending := false + + +func _enter_tree() -> void: + var filesystem := get_editor_interface().get_resource_filesystem() + if not filesystem.resources_reimported.is_connected(_on_resources_reimported): + filesystem.resources_reimported.connect(_on_resources_reimported) + + +func _exit_tree() -> void: + var filesystem := get_editor_interface().get_resource_filesystem() + if filesystem.resources_reimported.is_connected(_on_resources_reimported): + filesystem.resources_reimported.disconnect(_on_resources_reimported) + + +func _on_resources_reimported(resources: PackedStringArray) -> void: + if not resources.has(TERRAIN_RESOURCE) or _bake_pending: + return + _bake_pending = true + call_deferred("_bake_configured_shorelines") + + +func _bake_configured_shorelines() -> void: + _bake_pending = false + var packed_region := ResourceLoader.load( + REGION_SCENE, + "PackedScene", + ResourceLoader.CACHE_MODE_REPLACE, + ) as PackedScene + if packed_region == null: + push_error("Shoreline auto-bake could not load %s." % REGION_SCENE) + return + var region := packed_region.instantiate() as Node3D + add_child(region) + var baker := region.get_node_or_null( + "ShorelineRibbonBaker" + ) as ShorelineRibbonBaker + if baker == null: + push_error("Shoreline auto-bake found no configured baker.") + region.queue_free() + return + var results := baker.rebuild_all() + for result: Dictionary in results: + if result.get("skipped", false): + continue + var output_path: String = result.get("output_path", "") + if not output_path.is_empty(): + ResourceLoader.load( + output_path, + "ArrayMesh", + ResourceLoader.CACHE_MODE_REPLACE, + ) + print( + "Shoreline auto-bake: %s, %d loops, %d triangles" + % [output_path, result.loop_count, result.triangle_count] + ) + region.queue_free() + get_editor_interface().get_resource_filesystem().scan() diff --git a/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd.uid b/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd.uid new file mode 100644 index 0000000..a143fcb --- /dev/null +++ b/addons/netfishing_shoreline_baker/shoreline_auto_baker.gd.uid @@ -0,0 +1 @@ +uid://cbk4xpalei5v3 diff --git a/art/exported/characters/base/netfishing_base_character.glb b/art/exported/characters/base/netfishing_base_character.glb index a0ef899..b7bc2f7 100644 Binary files a/art/exported/characters/base/netfishing_base_character.glb and b/art/exported/characters/base/netfishing_base_character.glb differ diff --git a/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png b/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png new file mode 100644 index 0000000..36e6859 Binary files /dev/null and b/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png differ diff --git a/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import b/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import new file mode 100644 index 0000000..4141bca --- /dev/null +++ b/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyjwgkgw0syo8" +path.s3tc="res://.godot/imported/netfishing_base_character_eyes_simple_shine.png-140bfa3aa0b6f662e8ff9e5721213a61.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_base_character_eyes_simple_shine.png-140bfa3aa0b6f662e8ff9e5721213a61.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "c2f7c29aaaa95809e140eb308d9bb132" +} + +[deps] + +source_file="res://art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png" +dest_files=["res://.godot/imported/netfishing_base_character_eyes_simple_shine.png-140bfa3aa0b6f662e8ff9e5721213a61.s3tc.ctex", "res://.godot/imported/netfishing_base_character_eyes_simple_shine.png-140bfa3aa0b6f662e8ff9e5721213a61.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/exported/characters/base/netfishing_base_character_mouth_three.png b/art/exported/characters/base/netfishing_base_character_mouth_three.png new file mode 100644 index 0000000..527f300 Binary files /dev/null and b/art/exported/characters/base/netfishing_base_character_mouth_three.png differ diff --git a/art/exported/characters/base/netfishing_base_character_mouth_three.png.import b/art/exported/characters/base/netfishing_base_character_mouth_three.png.import new file mode 100644 index 0000000..10d0721 --- /dev/null +++ b/art/exported/characters/base/netfishing_base_character_mouth_three.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dt5gitdtjdvcd" +path.s3tc="res://.godot/imported/netfishing_base_character_mouth_three.png-90b4a98403d2a291a56a8ed42aaa8473.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_base_character_mouth_three.png-90b4a98403d2a291a56a8ed42aaa8473.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "f8bf25a1006c5cc2ab5323f4a758eec7" +} + +[deps] + +source_file="res://art/exported/characters/base/netfishing_base_character_mouth_three.png" +dest_files=["res://.godot/imported/netfishing_base_character_mouth_three.png-90b4a98403d2a291a56a8ed42aaa8473.s3tc.ctex", "res://.godot/imported/netfishing_base_character_mouth_three.png-90b4a98403d2a291a56a8ed42aaa8473.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/exported/characters/base/netfishing_base_character_nose_dog_round.png b/art/exported/characters/base/netfishing_base_character_nose_dog_round.png new file mode 100644 index 0000000..18a4590 Binary files /dev/null and b/art/exported/characters/base/netfishing_base_character_nose_dog_round.png differ diff --git a/art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import b/art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import new file mode 100644 index 0000000..71d25b6 --- /dev/null +++ b/art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmho1ryst3smt" +path.s3tc="res://.godot/imported/netfishing_base_character_nose_dog_round.png-0008adff5271dd0403e610ef13dd52cf.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_base_character_nose_dog_round.png-0008adff5271dd0403e610ef13dd52cf.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "3134f170152f9fa1512b41b61ecb01fc" +} + +[deps] + +source_file="res://art/exported/characters/base/netfishing_base_character_nose_dog_round.png" +dest_files=["res://.godot/imported/netfishing_base_character_nose_dog_round.png-0008adff5271dd0403e610ef13dd52cf.s3tc.ctex", "res://.godot/imported/netfishing_base_character_nose_dog_round.png-0008adff5271dd0403e610ef13dd52cf.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/exported/characters/faces/eyes/eyes_alligator_extra.png b/art/exported/characters/faces/eyes/eyes_alligator_extra.png new file mode 100644 index 0000000..e8c7be1 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_alligator_extra.png differ diff --git a/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import b/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import new file mode 100644 index 0000000..c9348c7 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4rj1r1doawb4" +path="res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_alligator_extra.png" +dest_files=["res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_alligator_eyes.png b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png new file mode 100644 index 0000000..74736eb Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png differ diff --git a/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import new file mode 100644 index 0000000..7f2eb8d --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://br2h5sfqmool8" +path="res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_alligator_eyes.png" +dest_files=["res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_angry.png b/art/exported/characters/faces/eyes/eyes_angry.png new file mode 100644 index 0000000..f88220e Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_angry.png differ diff --git a/art/exported/environment/textures/grass_heavy.png.import b/art/exported/characters/faces/eyes/eyes_angry.png.import similarity index 72% rename from art/exported/environment/textures/grass_heavy.png.import rename to art/exported/characters/faces/eyes/eyes_angry.png.import index d99e72b..3be46d5 100644 --- a/art/exported/environment/textures/grass_heavy.png.import +++ b/art/exported/characters/faces/eyes/eyes_angry.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dusa52a3eghr7" -path="res://.godot/imported/grass_heavy.png-ba7f679d1bc5402f8b0a3465ac6a44b8.ctex" +uid="uid://b1ieqa8qos1vk" +path="res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://art/exported/environment/textures/grass_heavy.png" -dest_files=["res://.godot/imported/grass_heavy.png-ba7f679d1bc5402f8b0a3465ac6a44b8.ctex"] +source_file="res://art/exported/characters/faces/eyes/eyes_angry.png" +dest_files=["res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.ctex"] [params] diff --git a/art/exported/characters/faces/eyes/eyes_anime_blue.png b/art/exported/characters/faces/eyes/eyes_anime_blue.png new file mode 100644 index 0000000..39a6b86 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_anime_blue.png differ diff --git a/art/exported/characters/faces/eyes/eyes_anime_blue.png.import b/art/exported/characters/faces/eyes/eyes_anime_blue.png.import new file mode 100644 index 0000000..443c8d0 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_anime_blue.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2ecbidhb33vy" +path="res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_anime_blue.png" +dest_files=["res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_anime_green.png b/art/exported/characters/faces/eyes/eyes_anime_green.png new file mode 100644 index 0000000..e273c6e Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_anime_green.png differ diff --git a/art/exported/characters/faces/eyes/eyes_anime_green.png.import b/art/exported/characters/faces/eyes/eyes_anime_green.png.import new file mode 100644 index 0000000..a1be2b0 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_anime_green.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwrxrtiv375ne" +path="res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_anime_green.png" +dest_files=["res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_beady.png b/art/exported/characters/faces/eyes/eyes_beady.png new file mode 100644 index 0000000..cdc5c2a Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_beady.png differ diff --git a/art/exported/characters/faces/eyes/eyes_beady.png.import b/art/exported/characters/faces/eyes/eyes_beady.png.import new file mode 100644 index 0000000..829845c --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_beady.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bahypju2dturm" +path="res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_beady.png" +dest_files=["res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_beady_extra.png b/art/exported/characters/faces/eyes/eyes_beady_extra.png new file mode 100644 index 0000000..437d483 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_beady_extra.png differ diff --git a/art/exported/characters/faces/eyes/eyes_beady_extra.png.import b/art/exported/characters/faces/eyes/eyes_beady_extra.png.import new file mode 100644 index 0000000..2951456 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_beady_extra.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqgdrdc0hpi7s" +path="res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_beady_extra.png" +dest_files=["res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png b/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png new file mode 100644 index 0000000..6c2a02b Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png differ diff --git a/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png.import b/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png.import new file mode 100644 index 0000000..fe258f0 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mvjcdk84ko6f" +path="res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_beady_extra_shine.png" +dest_files=["res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png new file mode 100644 index 0000000..80b1077 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png differ diff --git a/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import new file mode 100644 index 0000000..4368bec --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dp4sm67vn52um" +path="res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_beady_eyebrows.png" +dest_files=["res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_beady_whites.png b/art/exported/characters/faces/eyes/eyes_beady_whites.png new file mode 100644 index 0000000..960fe50 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_beady_whites.png differ diff --git a/art/exported/characters/faces/eyes/eyes_beady_whites.png.import b/art/exported/characters/faces/eyes/eyes_beady_whites.png.import new file mode 100644 index 0000000..2b6680e --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_beady_whites.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cw34wh51s5sxi" +path="res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_beady_whites.png" +dest_files=["res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_carrot_eyes.png b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png new file mode 100644 index 0000000..5a11a4b Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png differ diff --git a/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import new file mode 100644 index 0000000..80ef07a --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vn7mkjkx80ix" +path="res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_carrot_eyes.png" +dest_files=["res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_cat_real.png b/art/exported/characters/faces/eyes/eyes_cat_real.png new file mode 100644 index 0000000..365dd61 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_cat_real.png differ diff --git a/art/exported/characters/faces/eyes/eyes_cat_real.png.import b/art/exported/characters/faces/eyes/eyes_cat_real.png.import new file mode 100644 index 0000000..2a4cbd3 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_cat_real.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://sbxopn55femq" +path="res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_cat_real.png" +dest_files=["res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png b/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png new file mode 100644 index 0000000..17e6c94 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png differ diff --git a/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png.import b/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png.import new file mode 100644 index 0000000..9d9b065 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cg1l01m0ae2oa" +path="res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_cat_real_hunter.png" +dest_files=["res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_close.png b/art/exported/characters/faces/eyes/eyes_close.png new file mode 100644 index 0000000..1e0123f Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_close.png differ diff --git a/art/exported/characters/faces/eyes/eyes_close.png.import b/art/exported/characters/faces/eyes/eyes_close.png.import new file mode 100644 index 0000000..25986e6 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_close.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kbigjk76ejx8" +path="res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_close.png" +dest_files=["res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_closed.png b/art/exported/characters/faces/eyes/eyes_closed.png new file mode 100644 index 0000000..d37f0a7 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_closed.png differ diff --git a/art/exported/characters/faces/eyes/eyes_closed.png.import b/art/exported/characters/faces/eyes/eyes_closed.png.import new file mode 100644 index 0000000..af51540 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_closed.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2ufsni8mnpmq" +path="res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_closed.png" +dest_files=["res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_cute.png b/art/exported/characters/faces/eyes/eyes_cute.png new file mode 100644 index 0000000..1e078b9 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_cute.png differ diff --git a/art/exported/environment/textures/cliff_wall.png.import b/art/exported/characters/faces/eyes/eyes_cute.png.import similarity index 72% rename from art/exported/environment/textures/cliff_wall.png.import rename to art/exported/characters/faces/eyes/eyes_cute.png.import index 026f231..c8d9f09 100644 --- a/art/exported/environment/textures/cliff_wall.png.import +++ b/art/exported/characters/faces/eyes/eyes_cute.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cd7qwsbeg1jxx" -path="res://.godot/imported/cliff_wall.png-7ce756d49c24c7b8fdda307d68086d87.ctex" +uid="uid://b5455dbwse02f" +path="res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://art/exported/environment/textures/cliff_wall.png" -dest_files=["res://.godot/imported/cliff_wall.png-7ce756d49c24c7b8fdda307d68086d87.ctex"] +source_file="res://art/exported/characters/faces/eyes/eyes_cute.png" +dest_files=["res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.ctex"] [params] diff --git a/art/exported/characters/faces/eyes/eyes_cute_close.png b/art/exported/characters/faces/eyes/eyes_cute_close.png new file mode 100644 index 0000000..c49f1c5 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_cute_close.png differ diff --git a/art/exported/characters/faces/eyes/eyes_cute_close.png.import b/art/exported/characters/faces/eyes/eyes_cute_close.png.import new file mode 100644 index 0000000..5b51dc2 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_cute_close.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lp21yuhfqbc1" +path="res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_cute_close.png" +dest_files=["res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_huge.png b/art/exported/characters/faces/eyes/eyes_huge.png new file mode 100644 index 0000000..5c1026f Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_huge.png differ diff --git a/art/exported/characters/faces/eyes/eyes_huge.png.import b/art/exported/characters/faces/eyes/eyes_huge.png.import new file mode 100644 index 0000000..2c449bd --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_huge.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vb1eaupu8fpm" +path="res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_huge.png" +dest_files=["res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_interested.png b/art/exported/characters/faces/eyes/eyes_interested.png new file mode 100644 index 0000000..4e1239e Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_interested.png differ diff --git a/art/exported/characters/faces/eyes/eyes_interested.png.import b/art/exported/characters/faces/eyes/eyes_interested.png.import new file mode 100644 index 0000000..061fe34 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_interested.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3481384do6m4" +path="res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_interested.png" +dest_files=["res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern.png b/art/exported/characters/faces/eyes/eyes_jackolantern.png new file mode 100644 index 0000000..98a0492 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_jackolantern.png differ diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern.png.import b/art/exported/characters/faces/eyes/eyes_jackolantern.png.import new file mode 100644 index 0000000..c447cdd --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_jackolantern.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdqqfoss11muq" +path="res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_jackolantern.png" +dest_files=["res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern_small.png b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png new file mode 100644 index 0000000..1bb27fe Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png differ diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import new file mode 100644 index 0000000..8de58a2 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6n6riqe60s2f" +path="res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_jackolantern_small.png" +dest_files=["res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_punk_purple.png b/art/exported/characters/faces/eyes/eyes_punk_purple.png new file mode 100644 index 0000000..2ab8a17 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_punk_purple.png differ diff --git a/art/exported/characters/faces/eyes/eyes_punk_purple.png.import b/art/exported/characters/faces/eyes/eyes_punk_purple.png.import new file mode 100644 index 0000000..547a560 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_punk_purple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2c784my766ag" +path="res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_punk_purple.png" +dest_files=["res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_sexy_blue.png b/art/exported/characters/faces/eyes/eyes_sexy_blue.png new file mode 100644 index 0000000..3dffd6c Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_sexy_blue.png differ diff --git a/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import b/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import new file mode 100644 index 0000000..7d2046d --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drajn1tk8gq4a" +path="res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_sexy_blue.png" +dest_files=["res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_sexy_brown.png b/art/exported/characters/faces/eyes/eyes_sexy_brown.png new file mode 100644 index 0000000..bc53f8f Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_sexy_brown.png differ diff --git a/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import b/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import new file mode 100644 index 0000000..a2b30f1 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c38u5tdksowpw" +path="res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_sexy_brown.png" +dest_files=["res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_spider.png b/art/exported/characters/faces/eyes/eyes_spider.png new file mode 100644 index 0000000..239707f Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_spider.png differ diff --git a/art/exported/characters/faces/eyes/eyes_spider.png.import b/art/exported/characters/faces/eyes/eyes_spider.png.import new file mode 100644 index 0000000..3b137b9 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_spider.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bevrdbag3eixb" +path="res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_spider.png" +dest_files=["res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_surprised.png b/art/exported/characters/faces/eyes/eyes_surprised.png new file mode 100644 index 0000000..be78ec8 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_surprised.png differ diff --git a/art/exported/characters/faces/eyes/eyes_surprised.png.import b/art/exported/characters/faces/eyes/eyes_surprised.png.import new file mode 100644 index 0000000..0d7907b --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_surprised.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://chvod4aqeqn10" +path="res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_surprised.png" +dest_files=["res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_tired.png b/art/exported/characters/faces/eyes/eyes_tired.png new file mode 100644 index 0000000..061b3e4 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_tired.png differ diff --git a/art/exported/characters/faces/eyes/eyes_tired.png.import b/art/exported/characters/faces/eyes/eyes_tired.png.import new file mode 100644 index 0000000..9de4b0f --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_tired.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bimnlu17t6u6v" +path="res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_tired.png" +dest_files=["res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_tired_flat.png b/art/exported/characters/faces/eyes/eyes_tired_flat.png new file mode 100644 index 0000000..faf123c Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_tired_flat.png differ diff --git a/art/exported/characters/faces/eyes/eyes_tired_flat.png.import b/art/exported/characters/faces/eyes/eyes_tired_flat.png.import new file mode 100644 index 0000000..5a2ec55 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_tired_flat.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4hmrvoblu3cl" +path="res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_tired_flat.png" +dest_files=["res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/eyes/eyes_upset.png b/art/exported/characters/faces/eyes/eyes_upset.png new file mode 100644 index 0000000..1e570a3 Binary files /dev/null and b/art/exported/characters/faces/eyes/eyes_upset.png differ diff --git a/art/exported/characters/faces/eyes/eyes_upset.png.import b/art/exported/characters/faces/eyes/eyes_upset.png.import new file mode 100644 index 0000000..0c455d4 --- /dev/null +++ b/art/exported/characters/faces/eyes/eyes_upset.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://blm1i6xyjclp5" +path="res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/eyes/eyes_upset.png" +dest_files=["res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_3.png b/art/exported/characters/faces/mouth/mouth_3.png new file mode 100644 index 0000000..dcdb30c Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_3.png differ diff --git a/art/exported/environment/textures/dirt_wall.png.import b/art/exported/characters/faces/mouth/mouth_3.png.import similarity index 73% rename from art/exported/environment/textures/dirt_wall.png.import rename to art/exported/characters/faces/mouth/mouth_3.png.import index 1b09653..eab9f48 100644 --- a/art/exported/environment/textures/dirt_wall.png.import +++ b/art/exported/characters/faces/mouth/mouth_3.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c3j0gailr6yfy" -path="res://.godot/imported/dirt_wall.png-8ba7bb24831d83f929bfa44b53af9288.ctex" +uid="uid://b5wnxcm61q4ou" +path="res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://art/exported/environment/textures/dirt_wall.png" -dest_files=["res://.godot/imported/dirt_wall.png-8ba7bb24831d83f929bfa44b53af9288.ctex"] +source_file="res://art/exported/characters/faces/mouth/mouth_3.png" +dest_files=["res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.ctex"] [params] diff --git a/art/exported/characters/faces/mouth/mouth_3_cheeky.png b/art/exported/characters/faces/mouth/mouth_3_cheeky.png new file mode 100644 index 0000000..b5f9797 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_3_cheeky.png differ diff --git a/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import b/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import new file mode 100644 index 0000000..a53b280 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhaec7illng60" +path="res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_3_cheeky.png" +dest_files=["res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_buck teeth.png b/art/exported/characters/faces/mouth/mouth_buck teeth.png new file mode 100644 index 0000000..9fde3c3 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_buck teeth.png differ diff --git a/art/exported/characters/faces/mouth/mouth_buck teeth.png.import b/art/exported/characters/faces/mouth/mouth_buck teeth.png.import new file mode 100644 index 0000000..8909223 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_buck teeth.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dmvxpg0jbiyqw" +path="res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_buck teeth.png" +dest_files=["res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_cheeky_smile.png b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png new file mode 100644 index 0000000..addb335 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png differ diff --git a/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import new file mode 100644 index 0000000..926ef6c --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8tdtfnv3bgft" +path="res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_cheeky_smile.png" +dest_files=["res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png b/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png new file mode 100644 index 0000000..cef99aa Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png differ diff --git a/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png.import b/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png.import new file mode 100644 index 0000000..0b1450a --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8xyawmp5gru8" +path="res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png" +dest_files=["res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_jackolantern.png b/art/exported/characters/faces/mouth/mouth_jackolantern.png new file mode 100644 index 0000000..80b5ebe Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_jackolantern.png differ diff --git a/art/exported/characters/faces/mouth/mouth_jackolantern.png.import b/art/exported/characters/faces/mouth/mouth_jackolantern.png.import new file mode 100644 index 0000000..1756a04 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_jackolantern.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ck4bbdnpf6ekd" +path="res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_jackolantern.png" +dest_files=["res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_jowls.png b/art/exported/characters/faces/mouth/mouth_jowls.png new file mode 100644 index 0000000..05ecbd1 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_jowls.png differ diff --git a/art/exported/characters/faces/mouth/mouth_jowls.png.import b/art/exported/characters/faces/mouth/mouth_jowls.png.import new file mode 100644 index 0000000..1af65fe --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_jowls.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyynmci0hf3vq" +path="res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_jowls.png" +dest_files=["res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_jowls_brock.png b/art/exported/characters/faces/mouth/mouth_jowls_brock.png new file mode 100644 index 0000000..3b3d4a3 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_jowls_brock.png differ diff --git a/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import b/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import new file mode 100644 index 0000000..d2ec5c4 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://duajvlqrieray" +path="res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_jowls_brock.png" +dest_files=["res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_jowls_simple.png b/art/exported/characters/faces/mouth/mouth_jowls_simple.png new file mode 100644 index 0000000..f96856e Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_jowls_simple.png differ diff --git a/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import b/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import new file mode 100644 index 0000000..f6cf4a0 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c37gty8itl5oe" +path="res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_jowls_simple.png" +dest_files=["res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_kissy.png b/art/exported/characters/faces/mouth/mouth_kissy.png new file mode 100644 index 0000000..65af5d9 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_kissy.png differ diff --git a/art/exported/characters/faces/mouth/mouth_kissy.png.import b/art/exported/characters/faces/mouth/mouth_kissy.png.import new file mode 100644 index 0000000..ff0e1c1 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_kissy.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://druncaq416lvu" +path="res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_kissy.png" +dest_files=["res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_kissy_lips.png b/art/exported/characters/faces/mouth/mouth_kissy_lips.png new file mode 100644 index 0000000..b20c0d1 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_kissy_lips.png differ diff --git a/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import b/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import new file mode 100644 index 0000000..2141904 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://babmei7rq8uaf" +path="res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_kissy_lips.png" +dest_files=["res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_lil_c.png b/art/exported/characters/faces/mouth/mouth_lil_c.png new file mode 100644 index 0000000..bd1219a Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_lil_c.png differ diff --git a/art/exported/characters/faces/mouth/mouth_lil_c.png.import b/art/exported/characters/faces/mouth/mouth_lil_c.png.import new file mode 100644 index 0000000..1a7c28d --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_lil_c.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bfafrjmy6liy6" +path="res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_lil_c.png" +dest_files=["res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_open_ah.png b/art/exported/characters/faces/mouth/mouth_open_ah.png new file mode 100644 index 0000000..b028f06 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_open_ah.png differ diff --git a/art/exported/characters/faces/mouth/mouth_open_ah.png.import b/art/exported/characters/faces/mouth/mouth_open_ah.png.import new file mode 100644 index 0000000..5f8c2e7 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_open_ah.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://44cqu18imydc" +path="res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_open_ah.png" +dest_files=["res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_open_oh.png b/art/exported/characters/faces/mouth/mouth_open_oh.png new file mode 100644 index 0000000..eba37ab Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_open_oh.png differ diff --git a/art/exported/characters/faces/mouth/mouth_open_oh.png.import b/art/exported/characters/faces/mouth/mouth_open_oh.png.import new file mode 100644 index 0000000..e4602aa --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_open_oh.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c78hwv0t3ph47" +path="res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_open_oh.png" +dest_files=["res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_open_small.png b/art/exported/characters/faces/mouth/mouth_open_small.png new file mode 100644 index 0000000..97c1755 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_open_small.png differ diff --git a/art/exported/characters/faces/mouth/mouth_open_small.png.import b/art/exported/characters/faces/mouth/mouth_open_small.png.import new file mode 100644 index 0000000..9b15e51 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_open_small.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bn68lfabdo7ga" +path="res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_open_small.png" +dest_files=["res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_open_smile.png b/art/exported/characters/faces/mouth/mouth_open_smile.png new file mode 100644 index 0000000..57675f8 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_open_smile.png differ diff --git a/art/exported/characters/faces/mouth/mouth_open_smile.png.import b/art/exported/characters/faces/mouth/mouth_open_smile.png.import new file mode 100644 index 0000000..06e410b --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_open_smile.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b12jrc4ao3r16" +path="res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_open_smile.png" +dest_files=["res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_p_tounge.png b/art/exported/characters/faces/mouth/mouth_p_tounge.png new file mode 100644 index 0000000..7e71399 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_p_tounge.png differ diff --git a/art/exported/characters/faces/mouth/mouth_p_tounge.png.import b/art/exported/characters/faces/mouth/mouth_p_tounge.png.import new file mode 100644 index 0000000..1a791ce --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_p_tounge.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nt1q6gpij6y3" +path="res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_p_tounge.png" +dest_files=["res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_sad.png b/art/exported/characters/faces/mouth/mouth_sad.png new file mode 100644 index 0000000..86687e0 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_sad.png differ diff --git a/art/exported/characters/faces/mouth/mouth_sad.png.import b/art/exported/characters/faces/mouth/mouth_sad.png.import new file mode 100644 index 0000000..bfe50b3 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_sad.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://camwvowsgxnlw" +path="res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_sad.png" +dest_files=["res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_sigh.png b/art/exported/characters/faces/mouth/mouth_sigh.png new file mode 100644 index 0000000..875da79 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_sigh.png differ diff --git a/art/exported/characters/faces/mouth/mouth_sigh.png.import b/art/exported/characters/faces/mouth/mouth_sigh.png.import new file mode 100644 index 0000000..928a5d2 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_sigh.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0842jxg3m1ca" +path="res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_sigh.png" +dest_files=["res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_simple_smile.png b/art/exported/characters/faces/mouth/mouth_simple_smile.png new file mode 100644 index 0000000..9f8d430 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_simple_smile.png differ diff --git a/art/exported/characters/faces/mouth/mouth_simple_smile.png.import b/art/exported/characters/faces/mouth/mouth_simple_smile.png.import new file mode 100644 index 0000000..82bd51a --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_simple_smile.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nafybekpya31" +path="res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_simple_smile.png" +dest_files=["res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_slanted.png b/art/exported/characters/faces/mouth/mouth_slanted.png new file mode 100644 index 0000000..568eb4f Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_slanted.png differ diff --git a/art/exported/characters/faces/mouth/mouth_slanted.png.import b/art/exported/characters/faces/mouth/mouth_slanted.png.import new file mode 100644 index 0000000..01efb87 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_slanted.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nuj6ednwjawn" +path="res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_slanted.png" +dest_files=["res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_squigle.png b/art/exported/characters/faces/mouth/mouth_squigle.png new file mode 100644 index 0000000..cffb2e8 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_squigle.png differ diff --git a/art/exported/characters/faces/mouth/mouth_squigle.png.import b/art/exported/characters/faces/mouth/mouth_squigle.png.import new file mode 100644 index 0000000..980021f --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_squigle.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://calw27urbe3hk" +path="res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_squigle.png" +dest_files=["res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_vampy.png b/art/exported/characters/faces/mouth/mouth_vampy.png new file mode 100644 index 0000000..ee941f2 Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_vampy.png differ diff --git a/art/exported/characters/faces/mouth/mouth_vampy.png.import b/art/exported/characters/faces/mouth/mouth_vampy.png.import new file mode 100644 index 0000000..d191c95 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_vampy.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdpe0lrftqjkq" +path="res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_vampy.png" +dest_files=["res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_whiskers.png b/art/exported/characters/faces/mouth/mouth_whiskers.png new file mode 100644 index 0000000..414634f Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_whiskers.png differ diff --git a/art/exported/characters/faces/mouth/mouth_whiskers.png.import b/art/exported/characters/faces/mouth/mouth_whiskers.png.import new file mode 100644 index 0000000..7fe6438 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_whiskers.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cpxwladf5vobh" +path="res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_whiskers.png" +dest_files=["res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_y.png b/art/exported/characters/faces/mouth/mouth_y.png new file mode 100644 index 0000000..600cd8c Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_y.png differ diff --git a/art/exported/characters/faces/mouth/mouth_y.png.import b/art/exported/characters/faces/mouth/mouth_y.png.import new file mode 100644 index 0000000..989fa6b --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_y.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6w65l4rjbgq" +path="res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_y.png" +dest_files=["res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_y_small.png b/art/exported/characters/faces/mouth/mouth_y_small.png new file mode 100644 index 0000000..f66357b Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_y_small.png differ diff --git a/art/exported/characters/faces/mouth/mouth_y_small.png.import b/art/exported/characters/faces/mouth/mouth_y_small.png.import new file mode 100644 index 0000000..205579e --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_y_small.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cchv8fvnqbtm2" +path="res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_y_small.png" +dest_files=["res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/mouth/mouth_y_small_pink.png b/art/exported/characters/faces/mouth/mouth_y_small_pink.png new file mode 100644 index 0000000..7f0af4a Binary files /dev/null and b/art/exported/characters/faces/mouth/mouth_y_small_pink.png differ diff --git a/art/exported/characters/faces/mouth/mouth_y_small_pink.png.import b/art/exported/characters/faces/mouth/mouth_y_small_pink.png.import new file mode 100644 index 0000000..f8237f1 --- /dev/null +++ b/art/exported/characters/faces/mouth/mouth_y_small_pink.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dvqdmhhiet5ix" +path="res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/mouth/mouth_y_small_pink.png" +dest_files=["res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_cat_real.png b/art/exported/characters/faces/noses/nose_cat_real.png new file mode 100644 index 0000000..0765f2e Binary files /dev/null and b/art/exported/characters/faces/noses/nose_cat_real.png differ diff --git a/art/exported/characters/faces/noses/nose_cat_real.png.import b/art/exported/characters/faces/noses/nose_cat_real.png.import new file mode 100644 index 0000000..aaedb17 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_cat_real.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5n0kihgof38n" +path="res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_cat_real.png" +dest_files=["res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_clown.png b/art/exported/characters/faces/noses/nose_clown.png new file mode 100644 index 0000000..cb9fb14 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_clown.png differ diff --git a/art/exported/characters/faces/noses/nose_clown.png.import b/art/exported/characters/faces/noses/nose_clown.png.import new file mode 100644 index 0000000..a9afce1 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_clown.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cc0nb34t7helb" +path="res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_clown.png" +dest_files=["res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_dog_real.png b/art/exported/characters/faces/noses/nose_dog_real.png new file mode 100644 index 0000000..01b3a7e Binary files /dev/null and b/art/exported/characters/faces/noses/nose_dog_real.png differ diff --git a/art/exported/characters/faces/noses/nose_dog_real.png.import b/art/exported/characters/faces/noses/nose_dog_real.png.import new file mode 100644 index 0000000..9fe82cc --- /dev/null +++ b/art/exported/characters/faces/noses/nose_dog_real.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b8jwky86fl3ky" +path="res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_dog_real.png" +dest_files=["res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_dog_simple.png b/art/exported/characters/faces/noses/nose_dog_simple.png new file mode 100644 index 0000000..d3743c7 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_dog_simple.png differ diff --git a/art/exported/characters/faces/noses/nose_dog_simple.png.import b/art/exported/characters/faces/noses/nose_dog_simple.png.import new file mode 100644 index 0000000..f61b74a --- /dev/null +++ b/art/exported/characters/faces/noses/nose_dog_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6d4ubqjijjux" +path="res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_dog_simple.png" +dest_files=["res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_dragon.png b/art/exported/characters/faces/noses/nose_dragon.png new file mode 100644 index 0000000..157a28d Binary files /dev/null and b/art/exported/characters/faces/noses/nose_dragon.png differ diff --git a/art/exported/characters/faces/noses/nose_dragon.png.import b/art/exported/characters/faces/noses/nose_dragon.png.import new file mode 100644 index 0000000..0ba2695 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_dragon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8n1bhbyt5id6" +path="res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_dragon.png" +dest_files=["res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_hippo.png b/art/exported/characters/faces/noses/nose_hippo.png new file mode 100644 index 0000000..7ceabb2 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_hippo.png differ diff --git a/art/exported/characters/faces/noses/nose_hippo.png.import b/art/exported/characters/faces/noses/nose_hippo.png.import new file mode 100644 index 0000000..0206809 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_hippo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmfa44uf6u4or" +path="res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_hippo.png" +dest_files=["res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_human.png b/art/exported/characters/faces/noses/nose_human.png new file mode 100644 index 0000000..e0e7846 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_human.png differ diff --git a/art/exported/characters/faces/noses/nose_human.png.import b/art/exported/characters/faces/noses/nose_human.png.import new file mode 100644 index 0000000..2381527 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_human.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cuthoacwggro3" +path="res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_human.png" +dest_files=["res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_nostrils.png b/art/exported/characters/faces/noses/nose_nostrils.png new file mode 100644 index 0000000..11fb0a3 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_nostrils.png differ diff --git a/art/exported/characters/faces/noses/nose_nostrils.png.import b/art/exported/characters/faces/noses/nose_nostrils.png.import new file mode 100644 index 0000000..8e9c447 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_nostrils.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bvu2lox5px3ab" +path="res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_nostrils.png" +dest_files=["res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_piercing.png b/art/exported/characters/faces/noses/nose_piercing.png new file mode 100644 index 0000000..f35b3ac Binary files /dev/null and b/art/exported/characters/faces/noses/nose_piercing.png differ diff --git a/art/exported/characters/faces/noses/nose_piercing.png.import b/art/exported/characters/faces/noses/nose_piercing.png.import new file mode 100644 index 0000000..3584e75 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_piercing.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3w37sblwbd8b" +path="res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_piercing.png" +dest_files=["res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_pig_snout.png b/art/exported/characters/faces/noses/nose_pig_snout.png new file mode 100644 index 0000000..0e403cf Binary files /dev/null and b/art/exported/characters/faces/noses/nose_pig_snout.png differ diff --git a/art/exported/characters/faces/noses/nose_pig_snout.png.import b/art/exported/characters/faces/noses/nose_pig_snout.png.import new file mode 100644 index 0000000..5e34829 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_pig_snout.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bvran6dr0u22l" +path="res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_pig_snout.png" +dest_files=["res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_pig_snout_pink.png b/art/exported/characters/faces/noses/nose_pig_snout_pink.png new file mode 100644 index 0000000..6fe994f Binary files /dev/null and b/art/exported/characters/faces/noses/nose_pig_snout_pink.png differ diff --git a/art/exported/characters/faces/noses/nose_pig_snout_pink.png.import b/art/exported/characters/faces/noses/nose_pig_snout_pink.png.import new file mode 100644 index 0000000..52ab860 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_pig_snout_pink.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cyigqco0dp8ph" +path="res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_pig_snout_pink.png" +dest_files=["res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_squidward.png b/art/exported/characters/faces/noses/nose_squidward.png new file mode 100644 index 0000000..a878daa Binary files /dev/null and b/art/exported/characters/faces/noses/nose_squidward.png differ diff --git a/art/exported/characters/faces/noses/nose_squidward.png.import b/art/exported/characters/faces/noses/nose_squidward.png.import new file mode 100644 index 0000000..dbba070 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_squidward.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ilvb5vre4kv7" +path="res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_squidward.png" +dest_files=["res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_squidward_wide.png b/art/exported/characters/faces/noses/nose_squidward_wide.png new file mode 100644 index 0000000..1f55a69 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_squidward_wide.png differ diff --git a/art/exported/characters/faces/noses/nose_squidward_wide.png.import b/art/exported/characters/faces/noses/nose_squidward_wide.png.import new file mode 100644 index 0000000..92d6c8a --- /dev/null +++ b/art/exported/characters/faces/noses/nose_squidward_wide.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dtisqywq2rv14" +path="res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_squidward_wide.png" +dest_files=["res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_triangle.png b/art/exported/characters/faces/noses/nose_triangle.png new file mode 100644 index 0000000..5f15f3a Binary files /dev/null and b/art/exported/characters/faces/noses/nose_triangle.png differ diff --git a/art/exported/characters/faces/noses/nose_triangle.png.import b/art/exported/characters/faces/noses/nose_triangle.png.import new file mode 100644 index 0000000..bc7aa21 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_triangle.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bitins3gtewe" +path="res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_triangle.png" +dest_files=["res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_triangle_pink.png b/art/exported/characters/faces/noses/nose_triangle_pink.png new file mode 100644 index 0000000..2915456 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_triangle_pink.png differ diff --git a/art/exported/characters/faces/noses/nose_triangle_pink.png.import b/art/exported/characters/faces/noses/nose_triangle_pink.png.import new file mode 100644 index 0000000..56712ab --- /dev/null +++ b/art/exported/characters/faces/noses/nose_triangle_pink.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://oi8qhxbtijmk" +path="res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_triangle_pink.png" +dest_files=["res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_triangle_small.png b/art/exported/characters/faces/noses/nose_triangle_small.png new file mode 100644 index 0000000..719e67b Binary files /dev/null and b/art/exported/characters/faces/noses/nose_triangle_small.png differ diff --git a/art/exported/characters/faces/noses/nose_triangle_small.png.import b/art/exported/characters/faces/noses/nose_triangle_small.png.import new file mode 100644 index 0000000..a659077 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_triangle_small.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cedgrca26ponk" +path="res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_triangle_small.png" +dest_files=["res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_triangle_small_pink.png b/art/exported/characters/faces/noses/nose_triangle_small_pink.png new file mode 100644 index 0000000..3a4bf19 Binary files /dev/null and b/art/exported/characters/faces/noses/nose_triangle_small_pink.png differ diff --git a/art/exported/characters/faces/noses/nose_triangle_small_pink.png.import b/art/exported/characters/faces/noses/nose_triangle_small_pink.png.import new file mode 100644 index 0000000..c875ba4 --- /dev/null +++ b/art/exported/characters/faces/noses/nose_triangle_small_pink.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0kird82ka1ug" +path="res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_triangle_small_pink.png" +dest_files=["res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/faces/noses/nose_under.png b/art/exported/characters/faces/noses/nose_under.png new file mode 100644 index 0000000..d7c4d7b Binary files /dev/null and b/art/exported/characters/faces/noses/nose_under.png differ diff --git a/art/exported/characters/faces/noses/nose_under.png.import b/art/exported/characters/faces/noses/nose_under.png.import new file mode 100644 index 0000000..5bf860f --- /dev/null +++ b/art/exported/characters/faces/noses/nose_under.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://u0ih35v73j5h" +path="res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/faces/noses/nose_under.png" +dest_files=["res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/bengal/body_arms_bengal.png b/art/exported/characters/patterns/bengal/body_arms_bengal.png new file mode 100644 index 0000000..dd4c735 Binary files /dev/null and b/art/exported/characters/patterns/bengal/body_arms_bengal.png differ diff --git a/art/exported/characters/patterns/bengal/body_arms_bengal.png.import b/art/exported/characters/patterns/bengal/body_arms_bengal.png.import new file mode 100644 index 0000000..8e98690 --- /dev/null +++ b/art/exported/characters/patterns/bengal/body_arms_bengal.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2e0uxfn187gu" +path="res://.godot/imported/body_arms_bengal.png-350ed15f218f0efa6ae7573ad27d3c65.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/bengal/body_arms_bengal.png" +dest_files=["res://.godot/imported/body_arms_bengal.png-350ed15f218f0efa6ae7573ad27d3c65.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/bengal/body_main_bengal.png b/art/exported/characters/patterns/bengal/body_main_bengal.png new file mode 100644 index 0000000..c6eae5b Binary files /dev/null and b/art/exported/characters/patterns/bengal/body_main_bengal.png differ diff --git a/art/exported/characters/patterns/bengal/body_main_bengal.png.import b/art/exported/characters/patterns/bengal/body_main_bengal.png.import new file mode 100644 index 0000000..389ec4f --- /dev/null +++ b/art/exported/characters/patterns/bengal/body_main_bengal.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cn71sswmj0tue" +path="res://.godot/imported/body_main_bengal.png-59815350b807220c52317e683582847e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/bengal/body_main_bengal.png" +dest_files=["res://.godot/imported/body_main_bengal.png-59815350b807220c52317e683582847e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/bengal/head_round_bengal.png b/art/exported/characters/patterns/bengal/head_round_bengal.png new file mode 100644 index 0000000..d5326c4 Binary files /dev/null and b/art/exported/characters/patterns/bengal/head_round_bengal.png differ diff --git a/art/exported/characters/patterns/bengal/head_round_bengal.png.import b/art/exported/characters/patterns/bengal/head_round_bengal.png.import new file mode 100644 index 0000000..aa5113b --- /dev/null +++ b/art/exported/characters/patterns/bengal/head_round_bengal.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rv36ws5wuakb" +path="res://.godot/imported/head_round_bengal.png-dfa502a4514c1605f5c8aff839dd8765.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/bengal/head_round_bengal.png" +dest_files=["res://.godot/imported/head_round_bengal.png-dfa502a4514c1605f5c8aff839dd8765.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/body_arms_fox.png b/art/exported/characters/patterns/fox/body_arms_fox.png new file mode 100644 index 0000000..aab265a Binary files /dev/null and b/art/exported/characters/patterns/fox/body_arms_fox.png differ diff --git a/art/exported/characters/patterns/fox/body_arms_fox.png.import b/art/exported/characters/patterns/fox/body_arms_fox.png.import new file mode 100644 index 0000000..af10b42 --- /dev/null +++ b/art/exported/characters/patterns/fox/body_arms_fox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://p5cg2bwrxf7v" +path="res://.godot/imported/body_arms_fox.png-91ab21ce544fdc29b8a0ebf3e54ef7cd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/fox/body_arms_fox.png" +dest_files=["res://.godot/imported/body_arms_fox.png-91ab21ce544fdc29b8a0ebf3e54ef7cd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/body_main_fox.png b/art/exported/characters/patterns/fox/body_main_fox.png new file mode 100644 index 0000000..5c87bc5 Binary files /dev/null and b/art/exported/characters/patterns/fox/body_main_fox.png differ diff --git a/art/exported/characters/patterns/fox/body_main_fox.png.import b/art/exported/characters/patterns/fox/body_main_fox.png.import new file mode 100644 index 0000000..467c2b8 --- /dev/null +++ b/art/exported/characters/patterns/fox/body_main_fox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wcsadkjjpaei" +path="res://.godot/imported/body_main_fox.png-25e4fa03a48053f6b37b3bbadb1f01ba.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/fox/body_main_fox.png" +dest_files=["res://.godot/imported/body_main_fox.png-25e4fa03a48053f6b37b3bbadb1f01ba.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/head_pointy_fox.png b/art/exported/characters/patterns/fox/head_pointy_fox.png new file mode 100644 index 0000000..71be5c3 Binary files /dev/null and b/art/exported/characters/patterns/fox/head_pointy_fox.png differ diff --git a/art/exported/characters/patterns/fox/head_pointy_fox.png.import b/art/exported/characters/patterns/fox/head_pointy_fox.png.import new file mode 100644 index 0000000..e3fa7e7 --- /dev/null +++ b/art/exported/characters/patterns/fox/head_pointy_fox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4vlbrp7qevvo" +path="res://.godot/imported/head_pointy_fox.png-ef3535a6c05a49aae36fe5514844a3df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/fox/head_pointy_fox.png" +dest_files=["res://.godot/imported/head_pointy_fox.png-ef3535a6c05a49aae36fe5514844a3df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/head_round_fox.png b/art/exported/characters/patterns/fox/head_round_fox.png new file mode 100644 index 0000000..2963975 Binary files /dev/null and b/art/exported/characters/patterns/fox/head_round_fox.png differ diff --git a/art/exported/characters/patterns/fox/head_round_fox.png.import b/art/exported/characters/patterns/fox/head_round_fox.png.import new file mode 100644 index 0000000..6d958dd --- /dev/null +++ b/art/exported/characters/patterns/fox/head_round_fox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwiwt76kyyevw" +path="res://.godot/imported/head_round_fox.png-27dc2125a4e5b85a622291762225a303.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/fox/head_round_fox.png" +dest_files=["res://.godot/imported/head_round_fox.png-27dc2125a4e5b85a622291762225a303.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/tails_fox_fox.png b/art/exported/characters/patterns/fox/tails_fox_fox.png new file mode 100644 index 0000000..7283c2c Binary files /dev/null and b/art/exported/characters/patterns/fox/tails_fox_fox.png differ diff --git a/art/exported/characters/patterns/fox/tails_fox_fox.png.import b/art/exported/characters/patterns/fox/tails_fox_fox.png.import new file mode 100644 index 0000000..46f6e60 --- /dev/null +++ b/art/exported/characters/patterns/fox/tails_fox_fox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cyr0wcavb1cmi" +path="res://.godot/imported/tails_fox_fox.png-061a224c36e90f10b33d37e616ff7e94.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/characters/patterns/fox/tails_fox_fox.png" +dest_files=["res://.godot/imported/tails_fox_fox.png-061a224c36e90f10b33d37e616ff7e94.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/environment/terrain/starter_island.glb b/art/exported/environment/terrain/starter_island.glb index 5d8b6ce..e812b19 100644 Binary files a/art/exported/environment/terrain/starter_island.glb and b/art/exported/environment/terrain/starter_island.glb differ diff --git a/art/exported/environment/textures/grass_lite.png.import b/art/exported/environment/textures/grass_lite.png.import deleted file mode 100644 index a5ec41c..0000000 --- a/art/exported/environment/textures/grass_lite.png.import +++ /dev/null @@ -1,41 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://crci2on30ctoc" -path.s3tc="res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://art/exported/environment/textures/grass_lite.png" -dest_files=["res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 diff --git a/art/exported/environment/textures/grass_olive_tile.png.import b/art/exported/environment/textures/grass_olive_tile.png.import deleted file mode 100644 index bd6ee69..0000000 --- a/art/exported/environment/textures/grass_olive_tile.png.import +++ /dev/null @@ -1,41 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://btxhq3rdlum2" -path.s3tc="res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://art/exported/environment/textures/grass_olive_tile.png" -dest_files=["res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 diff --git a/art/exported/environment/textures/sand.png.import b/art/exported/environment/textures/sand.png.import deleted file mode 100644 index 82f5791..0000000 --- a/art/exported/environment/textures/sand.png.import +++ /dev/null @@ -1,41 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bnje415gq85nn" -path.s3tc="res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://art/exported/environment/textures/sand.png" -dest_files=["res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 diff --git a/art/exported/system_icons/netfishing.ico b/art/exported/system_icons/netfishing.ico new file mode 100644 index 0000000..dc5bdae Binary files /dev/null and b/art/exported/system_icons/netfishing.ico differ diff --git a/art/exported/system_icons/netfishing_1024.png b/art/exported/system_icons/netfishing_1024.png new file mode 100644 index 0000000..a433e80 Binary files /dev/null and b/art/exported/system_icons/netfishing_1024.png differ diff --git a/art/exported/system_icons/netfishing_1024.png.import b/art/exported/system_icons/netfishing_1024.png.import new file mode 100644 index 0000000..08aa925 --- /dev/null +++ b/art/exported/system_icons/netfishing_1024.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkh5hw8wwu8pc" +path="res://.godot/imported/netfishing_1024.png-7c53a099ed2743b4a623c258641b1d29.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/system_icons/netfishing_1024.png" +dest_files=["res://.godot/imported/netfishing_1024.png-7c53a099ed2743b4a623c258641b1d29.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/system_icons/netfishing_256.png b/art/exported/system_icons/netfishing_256.png new file mode 100644 index 0000000..1e7be72 Binary files /dev/null and b/art/exported/system_icons/netfishing_256.png differ diff --git a/art/exported/system_icons/netfishing_256.png.import b/art/exported/system_icons/netfishing_256.png.import new file mode 100644 index 0000000..34d5d27 --- /dev/null +++ b/art/exported/system_icons/netfishing_256.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cty16m8ydla06" +path="res://.godot/imported/netfishing_256.png-d9dfc9c7871ac38bc73d09d00df679be.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/exported/system_icons/netfishing_256.png" +dest_files=["res://.godot/imported/netfishing_256.png-d9dfc9c7871ac38bc73d09d00df679be.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/patterns/pattern_moneyfish.png b/art/patterns/pattern_moneyfish.png index d223c44..6266f29 100644 Binary files a/art/patterns/pattern_moneyfish.png and b/art/patterns/pattern_moneyfish.png differ diff --git a/art/source/characters/base/netfishing_base_character.blend1 b/art/source/characters/base/netfishing_base_character.blend1 deleted file mode 100644 index e9ee0bb..0000000 Binary files a/art/source/characters/base/netfishing_base_character.blend1 and /dev/null differ diff --git a/art/source/characters/base/netfishing_character_source.blend b/art/source/characters/base/netfishing_character_source.blend new file mode 100644 index 0000000..e75f254 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source.blend differ diff --git a/art/source/characters/base/netfishing_character_source.blend.import b/art/source/characters/base/netfishing_character_source.blend.import new file mode 100644 index 0000000..8288280 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source.blend.import @@ -0,0 +1,63 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://d38y8blk2w5c5" +path="res://.godot/imported/netfishing_character_source.blend-1d18c95606937d4c8bcb87e334e02ee5.scn" + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source.blend" +dest_files=["res://.godot/imported/netfishing_character_source.blend-1d18c95606937d4c8bcb87e334e02ee5.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +blender/nodes/visible=0 +blender/nodes/active_collection_only=false +blender/nodes/punctual_lights=true +blender/nodes/cameras=true +blender/nodes/custom_properties=true +blender/nodes/modifiers=1 +blender/meshes/vertex_colors=1 +blender/meshes/uvs=true +blender/meshes/normals=true +blender/meshes/export_geometry_nodes_instances=false +blender/meshes/gpu_instances=false +blender/meshes/tangents=true +blender/meshes/skins=2 +blender/meshes/export_bones_deforming_mesh_only=false +blender/materials/unpack_enabled=true +blender/materials/export_materials=1 +blender/animation/limit_playback=true +blender/animation/always_sample=true +blender/animation/group_tracks=true +gltf/naming_version=2 +gltf/texture_map_mode=1 diff --git a/art/source/characters/base/netfishing_character_source.glb.import b/art/source/characters/base/netfishing_character_source.glb.import new file mode 100644 index 0000000..61158c5 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source.glb.import @@ -0,0 +1,45 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://3uq4cxiomttt" +path="res://.godot/imported/netfishing_character_source.glb-9c98ee44b142c5b3d06f0838b7d2c9c4.scn" + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source.glb" +dest_files=["res://.godot/imported/netfishing_character_source.glb-9c98ee44b142c5b3d06f0838b7d2c9c4.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +gltf/naming_version=2 +gltf/embedded_image_handling=1 +gltf/texture_map_mode=1 diff --git a/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png b/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png new file mode 100644 index 0000000..d381bf6 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png differ diff --git a/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import b/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import new file mode 100644 index 0000000..e7aade1 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdomsntfeovnc" +path.s3tc="res://.godot/imported/netfishing_character_source_eyes1_eyebrows1.png-a0b9f287e407afd9688e141dc5f2b801.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_eyes1_eyebrows1.png-a0b9f287e407afd9688e141dc5f2b801.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "58f88343d07131ad09a2872ff719be5e" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png" +dest_files=["res://.godot/imported/netfishing_character_source_eyes1_eyebrows1.png-a0b9f287e407afd9688e141dc5f2b801.s3tc.ctex", "res://.godot/imported/netfishing_character_source_eyes1_eyebrows1.png-a0b9f287e407afd9688e141dc5f2b801.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png b/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png new file mode 100644 index 0000000..36e6859 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png differ diff --git a/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import b/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import new file mode 100644 index 0000000..e49d401 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://du4u6meapay05" +path.s3tc="res://.godot/imported/netfishing_character_source_eyes_simple_shine.png-965743f3716320bbc385096956ffef2d.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_eyes_simple_shine.png-965743f3716320bbc385096956ffef2d.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "c2f7c29aaaa95809e140eb308d9bb132" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_eyes_simple_shine.png" +dest_files=["res://.godot/imported/netfishing_character_source_eyes_simple_shine.png-965743f3716320bbc385096956ffef2d.s3tc.ctex", "res://.godot/imported/netfishing_character_source_eyes_simple_shine.png-965743f3716320bbc385096956ffef2d.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/characters/base/netfishing_character_source_mouth1.png b/art/source/characters/base/netfishing_character_source_mouth1.png new file mode 100644 index 0000000..a230423 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_mouth1.png differ diff --git a/art/source/characters/base/netfishing_character_source_mouth1.png.import b/art/source/characters/base/netfishing_character_source_mouth1.png.import new file mode 100644 index 0000000..8ff49a4 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_mouth1.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://css0k0virgoph" +path.s3tc="res://.godot/imported/netfishing_character_source_mouth1.png-b323b9153acd3f552f35c1d1108567c8.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_mouth1.png-b323b9153acd3f552f35c1d1108567c8.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "5d01fd41e8705956256c951d0cb53924" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_mouth1.png" +dest_files=["res://.godot/imported/netfishing_character_source_mouth1.png-b323b9153acd3f552f35c1d1108567c8.s3tc.ctex", "res://.godot/imported/netfishing_character_source_mouth1.png-b323b9153acd3f552f35c1d1108567c8.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/characters/base/netfishing_character_source_mouth_three.png b/art/source/characters/base/netfishing_character_source_mouth_three.png new file mode 100644 index 0000000..527f300 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_mouth_three.png differ diff --git a/art/source/characters/base/netfishing_character_source_mouth_three.png.import b/art/source/characters/base/netfishing_character_source_mouth_three.png.import new file mode 100644 index 0000000..6abae09 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_mouth_three.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://di20c1x8vksi1" +path.s3tc="res://.godot/imported/netfishing_character_source_mouth_three.png-6bf79d4c9c660d30fb7fc365f0039313.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_mouth_three.png-6bf79d4c9c660d30fb7fc365f0039313.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "f8bf25a1006c5cc2ab5323f4a758eec7" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_mouth_three.png" +dest_files=["res://.godot/imported/netfishing_character_source_mouth_three.png-6bf79d4c9c660d30fb7fc365f0039313.s3tc.ctex", "res://.godot/imported/netfishing_character_source_mouth_three.png-6bf79d4c9c660d30fb7fc365f0039313.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/characters/base/netfishing_character_source_nose1.2.png b/art/source/characters/base/netfishing_character_source_nose1.2.png new file mode 100644 index 0000000..57896ed Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_nose1.2.png differ diff --git a/art/source/characters/base/netfishing_character_source_nose1.2.png.import b/art/source/characters/base/netfishing_character_source_nose1.2.png.import new file mode 100644 index 0000000..2fbb165 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_nose1.2.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7dwa0bfev1rk" +path.s3tc="res://.godot/imported/netfishing_character_source_nose1.2.png-ef4dd456c5b0f0db73b3dc063bfba941.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_nose1.2.png-ef4dd456c5b0f0db73b3dc063bfba941.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "ad576beb7a7035dde96eb8e8f3ffdee7" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_nose1.2.png" +dest_files=["res://.godot/imported/netfishing_character_source_nose1.2.png-ef4dd456c5b0f0db73b3dc063bfba941.s3tc.ctex", "res://.godot/imported/netfishing_character_source_nose1.2.png-ef4dd456c5b0f0db73b3dc063bfba941.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/characters/base/netfishing_character_source_nose_dog_round.png b/art/source/characters/base/netfishing_character_source_nose_dog_round.png new file mode 100644 index 0000000..18a4590 Binary files /dev/null and b/art/source/characters/base/netfishing_character_source_nose_dog_round.png differ diff --git a/art/source/characters/base/netfishing_character_source_nose_dog_round.png.import b/art/source/characters/base/netfishing_character_source_nose_dog_round.png.import new file mode 100644 index 0000000..12333e8 --- /dev/null +++ b/art/source/characters/base/netfishing_character_source_nose_dog_round.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0p55hwdry2cr" +path.s3tc="res://.godot/imported/netfishing_character_source_nose_dog_round.png-1c89150861ebff3f930698c1747a0369.s3tc.ctex" +path.etc2="res://.godot/imported/netfishing_character_source_nose_dog_round.png-1c89150861ebff3f930698c1747a0369.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "3134f170152f9fa1512b41b61ecb01fc" +} + +[deps] + +source_file="res://art/source/characters/base/netfishing_character_source_nose_dog_round.png" +dest_files=["res://.godot/imported/netfishing_character_source_nose_dog_round.png-1c89150861ebff3f930698c1747a0369.s3tc.ctex", "res://.godot/imported/netfishing_character_source_nose_dog_round.png-1c89150861ebff3f930698c1747a0369.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/art/source/environment/terrain/starter_island.blend b/art/source/environment/terrain/starter_island.blend index 9d3b513..8b82c4e 100644 Binary files a/art/source/environment/terrain/starter_island.blend and b/art/source/environment/terrain/starter_island.blend differ diff --git a/art/exported/environment/textures/cliff_wall.png b/art/source/environment/textures/cliff_wall.png similarity index 100% rename from art/exported/environment/textures/cliff_wall.png rename to art/source/environment/textures/cliff_wall.png diff --git a/art/source/environment/textures/dirt.png b/art/source/environment/textures/dirt.png new file mode 100644 index 0000000..588c8c6 Binary files /dev/null and b/art/source/environment/textures/dirt.png differ diff --git a/art/source/environment/textures/dirt_light_wall.png b/art/source/environment/textures/dirt_light_wall.png new file mode 100644 index 0000000..623dcd5 Binary files /dev/null and b/art/source/environment/textures/dirt_light_wall.png differ diff --git a/art/exported/environment/textures/dirt_wall.png b/art/source/environment/textures/dirt_wall.png similarity index 100% rename from art/exported/environment/textures/dirt_wall.png rename to art/source/environment/textures/dirt_wall.png diff --git a/art/exported/environment/textures/grass.png b/art/source/environment/textures/grass.png similarity index 100% rename from art/exported/environment/textures/grass.png rename to art/source/environment/textures/grass.png diff --git a/art/exported/environment/textures/grass_heavy.png b/art/source/environment/textures/grass_heavy.png similarity index 100% rename from art/exported/environment/textures/grass_heavy.png rename to art/source/environment/textures/grass_heavy.png diff --git a/art/exported/environment/textures/grass_lite.png b/art/source/environment/textures/grass_lite.png similarity index 100% rename from art/exported/environment/textures/grass_lite.png rename to art/source/environment/textures/grass_lite.png diff --git a/art/exported/environment/textures/grass_olive_tile.png b/art/source/environment/textures/grass_olive_tile.png similarity index 100% rename from art/exported/environment/textures/grass_olive_tile.png rename to art/source/environment/textures/grass_olive_tile.png diff --git a/art/exported/environment/textures/sand.png b/art/source/environment/textures/sand.png similarity index 100% rename from art/exported/environment/textures/sand.png rename to art/source/environment/textures/sand.png diff --git a/art/source/environment/textures/sand_dark.png b/art/source/environment/textures/sand_dark.png new file mode 100644 index 0000000..80633b5 Binary files /dev/null and b/art/source/environment/textures/sand_dark.png differ diff --git a/art/source/environment/textures/sand_lite.png b/art/source/environment/textures/sand_lite.png new file mode 100644 index 0000000..06f807a Binary files /dev/null and b/art/source/environment/textures/sand_lite.png differ diff --git a/art/source/environment/textures/sandstone_heavy.png b/art/source/environment/textures/sandstone_heavy.png new file mode 100644 index 0000000..1bb282c Binary files /dev/null and b/art/source/environment/textures/sandstone_heavy.png differ diff --git a/art/source/environment/textures/sandstone_lite.png b/art/source/environment/textures/sandstone_lite.png new file mode 100644 index 0000000..c329585 Binary files /dev/null and b/art/source/environment/textures/sandstone_lite.png differ diff --git a/audio/ambience/rain_loop_ontario.ogg b/audio/ambience/rain_loop_ontario.ogg new file mode 100644 index 0000000..179987b Binary files /dev/null and b/audio/ambience/rain_loop_ontario.ogg differ diff --git a/audio/ambience/rain_loop_ontario.ogg.import b/audio/ambience/rain_loop_ontario.ogg.import new file mode 100644 index 0000000..280fc22 --- /dev/null +++ b/audio/ambience/rain_loop_ontario.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://cfqskba5j2iig" +path="res://.godot/imported/rain_loop_ontario.ogg-c206e8a7ee5398c9fdc6d59462050964.oggvorbisstr" + +[deps] + +source_file="res://audio/ambience/rain_loop_ontario.ogg" +dest_files=["res://.godot/imported/rain_loop_ontario.ogg-c206e8a7ee5398c9fdc6d59462050964.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/audio/ambience/waves.wav b/audio/ambience/waves.wav new file mode 100644 index 0000000..15f258e Binary files /dev/null and b/audio/ambience/waves.wav differ diff --git a/audio/ambience/waves.wav.import b/audio/ambience/waves.wav.import new file mode 100644 index 0000000..5ff6ba8 --- /dev/null +++ b/audio/ambience/waves.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dck3tf8qkadea" +path="res://.godot/imported/waves.wav-90ffb886ce064ba838eebb25ec72a17e.sample" + +[deps] + +source_file="res://audio/ambience/waves.wav" +dest_files=["res://.godot/imported/waves.wav-90ffb886ce064ba838eebb25ec72a17e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=1 +edit/loop_begin=0 +edit/loop_end=1044956 +compress/mode=0 diff --git a/audio/music/world/craft.mp3 b/audio/music/world/craft.mp3 new file mode 100644 index 0000000..b73cd15 Binary files /dev/null and b/audio/music/world/craft.mp3 differ diff --git a/audio/music/world/craft.mp3.import b/audio/music/world/craft.mp3.import new file mode 100644 index 0000000..b8e054c --- /dev/null +++ b/audio/music/world/craft.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://c4y8puv0n6xk8" +path="res://.godot/imported/craft.mp3-ba8d0c0c16ba90585f5d8eb4f37eb87d.mp3str" + +[deps] + +source_file="res://audio/music/world/craft.mp3" +dest_files=["res://.godot/imported/craft.mp3-ba8d0c0c16ba90585f5d8eb4f37eb87d.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/audio/sfx/fishing/bobber.wav b/audio/sfx/fishing/bobber.wav new file mode 100644 index 0000000..8c9d916 Binary files /dev/null and b/audio/sfx/fishing/bobber.wav differ diff --git a/audio/sfx/fishing/bobber.wav.import b/audio/sfx/fishing/bobber.wav.import new file mode 100644 index 0000000..d5fc82d --- /dev/null +++ b/audio/sfx/fishing/bobber.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://sxydt4wnmrj3" +path="res://.godot/imported/bobber.wav-b435bb251a112445f1ddebcacc74804a.sample" + +[deps] + +source_file="res://audio/sfx/fishing/bobber.wav" +dest_files=["res://.godot/imported/bobber.wav-b435bb251a112445f1ddebcacc74804a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/audio/sfx/fishing/fighting.wav b/audio/sfx/fishing/fighting.wav new file mode 100644 index 0000000..f647069 Binary files /dev/null and b/audio/sfx/fishing/fighting.wav differ diff --git a/audio/sfx/fishing/fighting.wav.import b/audio/sfx/fishing/fighting.wav.import new file mode 100644 index 0000000..ec59561 --- /dev/null +++ b/audio/sfx/fishing/fighting.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://df6yg85cldetw" +path="res://.godot/imported/fighting.wav-1427843e18acf5134cc66d4a813033b6.sample" + +[deps] + +source_file="res://audio/sfx/fishing/fighting.wav" +dest_files=["res://.godot/imported/fighting.wav-1427843e18acf5134cc66d4a813033b6.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=1 +edit/loop_begin=0 +edit/loop_end=124487 +compress/mode=0 diff --git a/audio/sfx/fishing/reeling.wav b/audio/sfx/fishing/reeling.wav new file mode 100644 index 0000000..6be5d03 Binary files /dev/null and b/audio/sfx/fishing/reeling.wav differ diff --git a/audio/sfx/fishing/reeling.wav.import b/audio/sfx/fishing/reeling.wav.import new file mode 100644 index 0000000..e5e6749 --- /dev/null +++ b/audio/sfx/fishing/reeling.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://v4yjxtslbrd7" +path="res://.godot/imported/reeling.wav-97a9415ae2d9e7ca4c976bb15b10e6fd.sample" + +[deps] + +source_file="res://audio/sfx/fishing/reeling.wav" +dest_files=["res://.godot/imported/reeling.wav-97a9415ae2d9e7ca4c976bb15b10e6fd.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=1 +edit/loop_begin=0 +edit/loop_end=114628 +compress/mode=0 diff --git a/default_bus_layout.tres b/default_bus_layout.tres index ca38684..d780a19 100644 --- a/default_bus_layout.tres +++ b/default_bus_layout.tres @@ -13,3 +13,15 @@ bus/1/mute = false bus/1/bypass_fx = false bus/1/volume_db = 0.0 bus/1/send = &"Master" +bus/2/name = &"SFX" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"Master" +bus/3/name = &"Environment" +bus/3/solo = false +bus/3/mute = false +bus/3/bypass_fx = false +bus/3/volume_db = 0.0 +bus/3/send = &"Master" diff --git a/docs/ATTRIBUTION.md b/docs/ATTRIBUTION.md new file mode 100644 index 0000000..321b73d --- /dev/null +++ b/docs/ATTRIBUTION.md @@ -0,0 +1,327 @@ +# Attribution, licensing, and asset provenance + +This is the authoritative project record for ownership, credits, license +boundaries, third-party notices, asset provenance, and redistribution checks. +A Git commit proves when bytes entered the repository; it does not by itself +prove authorship or licensing. + +## Ownership and credits + +NETfishing is jointly owned, developed, and published by its creators, +publicly credited as Voyager and Endeavour and operating collectively as the +independent team Woofmeow. + +- Voyager: co-owner, developer, project director, creator of the game's 3D + art, and composer of the original title/ambient track + `audio/music/title/as_in_four_wolves.ogg`, world track + `audio/music/world/craft.mp3`, and animalese placeholder tones. +- Endeavour: co-owner, developer, and creator of the game's 2D art, including + the original fish, environment, UI, and other artwork. +- chillnfill: contributor of original 3D models for the character bodies and + arms, ears and tails, and multiple world props and decorative assets, + including trees and bridges. The contributor requested to be credited as + `chillnfill`. +- Tekgator: creator of the cooler-capacity, reel-speed, and barrier-power shop + icons. + +Voyager is also the original catalog porter credit in PortMaster metadata. +Third-party creators are credited with their individual records below. Those +credits do not imply endorsement of NETfishing, Woofmeow, or its owners. + +## License map + +Do not describe the complete repository as covered only by the GPL. + +| Material | Governing terms | +| --- | --- | +| Project-owned software code, shaders, code-oriented scene/resource configuration, tests, build tooling, and server software | GPL-3.0-or-later; source-root `LICENSE` and packaged `GPL-3.0-or-later.txt` | +| Project-owned artwork, textures, models, animation, music, audio, narrative content, characters, and other creative assets | Source-root or packaged `ASSET-LICENSE.md` | +| NETfishing and Woofmeow names, logos, icons, and official identity | Source-root or packaged `TRADEMARKS.md` | +| Third-party engines, fonts, sounds, and other external material | Their original terms, recorded below and in adjacent license texts where applicable | +| New outside contributions | Source-root `CONTRIBUTOR-TERMS.md` plus any separately signed agreement | + +Generated Godot import metadata and deterministic generated resources follow +the terms of their source material or generating code. Repository location +alone does not relicense third-party material. + +The GPL permits study, modification, redistribution, and forks of covered +code. It does not grant rights to NETfishing creative assets or branding. The +NETfishing Asset License separately permits qualifying noncommercial add-on +Mods for official NETfishing software; it does not permit those assets to +accompany code forks, clones, standalone games, unrelated products, or +general-purpose asset packs. A GPL fork must replace NETfishing assets and +reserved branding with independently licensed material and a distinct +identity. + +Project-wide code, asset, branding, or contributor-license changes require +written approval from both NETfishing owners under their private joint +ownership agreement. + +## Godot Engine notice + +NETfishing uses Godot Engine under the MIT License: + +> Copyright (c) 2014-present Godot Engine contributors. +> Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the “Software”), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +Godot licensing and bundled third-party-component notices are available at +https://godotengine.org/license/ and +https://github.com/godotengine/godot/blob/master/COPYRIGHT.txt. Godot uses +FreeType for font rendering; portions are copyright The FreeType Project +(https://www.freetype.org) under its applicable license. + +## Font notices + +- **Tuffy Bold** — Thatcher Ulrich, Karoly Barta, and Michael Evans; dedicated + to the public domain. Runtime file: `ui/fonts/Tuffy_Bold.otf`; original notice: + `ui/fonts/Tuffy-LICENSE.txt`. +- **Seattle Avenue** — JLH Fonts; dedicated to the public domain by the creator. + Runtime file: `ui/fonts/seattle_avenue.otf`; source, hash, and original notice: + `ui/fonts/Seattle-Avenue-LICENSE.txt`. + +Public-domain fonts are not governed by the NETfishing Asset License. + +## Intake policy + +For every new external or supplied asset, record: + +- creator or supplying party; +- original source location or delivery channel; +- source filename and repository destination; +- source and destination SHA-256 when copied without modification; +- license or explicit permission; +- any permitted transformations; +- the importing commit or release. + +Maintainers must also obtain affirmative agreement to the source-root +`CONTRIBUTOR-TERMS.md` before accepting an outside contribution and retain +that agreement in a durable pull request, issue, email, or signed document. + +Runtime resources must use `res://` paths. Workstation Sync folders are intake +locations only and must never appear in scenes or resources. + +## Asset inventory and provenance + +| Asset family | Repository location | Recorded provenance | +| --- | --- | --- | +| Fish art and portraits | `fish/species/`, `art/exported/fish/` | Original 2D artwork by co-owner Endeavour. | +| Item icons | `items/icons/` | Original 2D artwork by co-owner Endeavour except where separately credited below. | +| Cooler-capacity shop icon | `items/icons/equipment/64_cooler_plus.png` | Original artwork by Tekgator. Repository SHA-256: `b63705be5b78915753c068170323781a90e22b555b57a5b2ca9466e2726539f7`. | +| Reel-speed shop icon | `items/icons/shop/64_speed_plus.png` | Original artwork by Tekgator. Repository SHA-256: `938452798f2be505e06ce3fbef178f52459331fd3bced8763fbc2a80157125ca`. | +| Barrier-power shop icon | `items/icons/shop/64_power_plus.png` | Original artwork by Tekgator. Repository SHA-256: `164d34ce152aa6d75de79b69f0be2d9074f7891057de9636678889028ff98ae7`. | +| Inventory notepad | `art/ui/ui_notepad.png` | Original 2D artwork by co-owner Endeavour; integrated without a runtime external path. | +| Environment textures | `art/exported/environment/textures/` | Original 2D artwork by co-owner Endeavour. | +| UI patterns | `art/patterns/` | Original 2D artwork by co-owner Endeavour. | +| Fur customization channel maps | `art/exported/characters/patterns/` | Original 2D channel-map artwork by co-owner Endeavour. Exact file hashes are recorded below. | +| Character base meshes and appendages | `art/source/characters/base/`, `art/exported/characters/base/`, and derived character resources | Original body and arm models and original ear and tail models by chillnfill. Any embedded original 2D artwork is by Endeavour. | +| World and decorative prop models | `art/source/environment/`, `art/exported/environment/`, and derived world resources | Original prop and decorative models by chillnfill, including trees and bridges. Any embedded original 2D artwork is by Endeavour. | +| Tuffy font | `ui/fonts/Tuffy_Bold.otf` | Public-domain dedication in `ui/fonts/Tuffy-LICENSE.txt`. | +| Seattle Avenue font | `ui/fonts/seattle_avenue.otf` | Public-domain font by JLH Fonts; source record and hash in `ui/fonts/Seattle-Avenue-LICENSE.txt`. | +| Title music | `audio/music/title/as_in_four_wolves.ogg` | Original music composed and owned by co-owner Voyager. | +| Dusk music | `audio/music/world/craft.mp3` | Original music composed and owned by co-owner Voyager. | +| Fishing fight loop | `audio/sfx/fishing/fighting.wav` | Edited from “Spinning reel.wav” by Freesound user tosha73, sound 509902, CC0. | +| Manual-reeling loop | `audio/sfx/fishing/reeling.wav` | Edited from the same CC0 “Spinning reel.wav” source. | +| Saltwater wave ambience | `audio/ambience/waves.wav` | “Gentle Ocean Waves Loop” by Freesound user kkenny101, sound 852826, CC0. | +| Bobber water impact | `audio/sfx/fishing/bobber.wav` | “Quick Water Droplet” by Freesound user qubodup, sound 792931, CC0. | +| Rain ambience | `audio/ambience/rain_loop_ontario.ogg` | Converted from “Rain Loop Ontario” by Freesound user Ayton, sound 212799, CC BY 3.0. | +| Character bark call | `sound/dialogue/calls/bark.wav` | Edited from `Dog_Bark.wav` by Freesound user ivolipa, sound 328729, CC0. | +| Character meow call | `sound/dialogue/calls/meow.wav` | Edited from `Cat meow.m4a` by Freesound user Christyboy100, sound 495694, Attribution 3.0. | +| Animalese placeholder tones | `sound/dialogue/animalese/placeholder/` | Original tones composed and owned by co-owner Voyager without third-party samples. | + +Creative Commons Zero information: +https://creativecommons.org/publicdomain/zero/1.0/. Creative Commons +Attribution 3.0 legal text: +https://creativecommons.org/licenses/by/3.0/legalcode. + +### chillnfill 3D model contribution record + +- Requested credit: `chillnfill`. +- Original contribution families: character bodies and arms; character ears + and tails; and multiple world props and decorative assets, including trees + and bridges. +- Repository mapping: character contributions are incorporated into the + character source/export families identified above. Prop contributions may + be incorporated into combined environment source and export files rather + than retained as one file per originally supplied model. +- Permission record: because these historical contributions predate the + current `CONTRIBUTOR-TERMS.md`, retain the original permission record or a + later written confirmation covering Project use, modification, + distribution, commercial release, and applicable sublicensing. + +### Fur customization channel-map record + +Original channel-map artwork by Endeavour. Repository paths preserve the +authored `mesh_style.png` filenames. + +| Style and mesh | Source filename | Repository file | SHA-256 | +| --- | --- | --- | --- | +| Bengal spots, body and arms | `body_arms_bengal.png` | `art/exported/characters/patterns/bengal/body_arms_bengal.png` | `fa90bd4e74b0b770ce97bf69d069676f5d4bd1f5d649f89e09638a66b61a8470` | +| Bengal spots, main body | `body_main_bengal.png` | `art/exported/characters/patterns/bengal/body_main_bengal.png` | `8662fa198220cfdbe0aa83bdeeef7b855e20124d74f8481467f750d32b67ee8b` | +| Bengal spots, round head | `head_round_bengal.png` | `art/exported/characters/patterns/bengal/head_round_bengal.png` | `f397ed8d152e3fda8181757a2bbd01925b7b8c2c4362f515d91c7f0fbf3ddfb6` | +| Fox, body and arms | `body_arms_fox.png` | `art/exported/characters/patterns/fox/body_arms_fox.png` | `9930808718f31d84b415902f1ba55ac2a508bb2ac127300e793d73a8a013022d` | +| Fox, main body | `body_main_fox.png` | `art/exported/characters/patterns/fox/body_main_fox.png` | `c65e4390173b8c31c9396edd053f940f0f809e47e01d18042cd7babc712dde9d` | +| Fox, pointy head | `head_pointy_fox.png` | `art/exported/characters/patterns/fox/head_pointy_fox.png` | `71a9d2383e62884dbd3981460fcd985b18805fa907f61ffb16dce08ff9418ddf` | +| Fox, round head | `head_round_fox.png` | `art/exported/characters/patterns/fox/head_round_fox.png` | `86dd7af8a2346faf67359ad668a117a74e2408676137d6b0626403cfcf2aba73` | +| Fox tail | `tails_fox_fox.png` | `art/exported/characters/patterns/fox/tails_fox_fox.png` | `72da9673f038e2c3bb8aca19ed99932085fa3b162cab95366901c4e97153dd3e` | + +### Tekgator shop icon contribution record + +- Requested credit: `Tekgator`. +- Original contributions: cooler-capacity, reel-speed, and barrier-power shop + icons. +- Repository files and SHA-256: + - `items/icons/equipment/64_cooler_plus.png`: + `b63705be5b78915753c068170323781a90e22b555b57a5b2ca9466e2726539f7` + - `items/icons/shop/64_speed_plus.png`: + `938452798f2be505e06ce3fbef178f52459331fd3bced8763fbc2a80157125ca` + - `items/icons/shop/64_power_plus.png`: + `164d34ce152aa6d75de79b69f0be2d9074f7891057de9636678889028ff98ae7` +- Permission record: retain the original permission record or a later written + confirmation covering Project use, modification, distribution, commercial + release, and applicable sublicensing. + +### Fishing fight loop source record + +- Source page: https://freesound.org/s/509902/ +- Creator: tosha73 +- Source title: `Spinning reel.wav` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `509902__tosha73__spinning-reel.wav` +- Downloaded source SHA-256: + `9741afdb1ec9c73f5e039f0e5ac174998cb0a1f737b626531e63919d4d232fad` +- Audacity working project: `fighting.aup3` +- Audacity project SHA-256: + `9928f077ecf48d01416f8d05700707e1b07d0f731846db80e27116f5b82dbbf5` +- Runtime edit source/destination SHA-256: + `b6f3f2f94bfaf9254ac04dd96b1fd28c15e1011a1db44bb3e70eeaa95de3c212` +- Runtime format: 48 kHz, 16-bit, stereo PCM WAV; 2.593479 seconds; + configured as a forward loop by the fishing presentation. +- Manual-reeling Audacity project: `reeling.aup3` +- Manual-reeling Audacity project SHA-256: + `20ad52f6bdd16ae6561f06f8355fe309e95023e815053fd5dfbd5a5ce3c1f9f9` +- Manual-reeling runtime source/destination SHA-256: + `e403d5b7c8af1da4b28599cc0f51de3eda70d9e9296db27852fe7dc95617a29d` +- Manual-reeling runtime format: 48 kHz, 16-bit, stereo PCM WAV; + 2.388083 seconds; configured as a forward loop. +- The downloaded source and Audacity projects remain in the project owner's + source-work archive; only the finished runtime edits ship in the game. + +### Saltwater wave ambience source record + +- Source page: https://freesound.org/s/852826/ +- Creator: kkenny101 +- Source title: `Gentle Ocean Waves Loop` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: + `852826__kkenny101__gentle-ocean-waves-loop.wav` +- Downloaded source and runtime source/destination SHA-256: + `f93e9890583f072ac52cf197b8b5942397d3d5e889fbdb9112a0776142f50229` +- Runtime format: 48 kHz, 24-bit, mono PCM WAV; 21.769917 seconds; + configured as a forward loop. +- The downloaded source remains in the project owner's source-work archive; + only the finished runtime loop ships in the game. + +### Bobber water-impact source record + +- Source page: https://freesound.org/s/792931/ +- Creator: qubodup +- Source title: `Quick Water Droplet` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `792931__qubodup__quick-water-droplet.wav` +- Downloaded source and runtime source/destination SHA-256: + `aef0d16384efed4a7b071c4d80c21597617f882f4e0c1d5295ac194d315eafb7` +- Runtime format: 48 kHz, 16-bit, mono PCM WAV; 0.197146 seconds; one-shot. +- The downloaded source remains in the project owner's source-work archive; + only the finished runtime sound ships in the game. + +### Character-call source records + +#### Bark + +- Source page: https://freesound.org/s/328729/ +- Creator: ivolipa +- Source title: `Dog_Bark.wav` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `328729__ivolipa__dog_bark.wav` +- Downloaded source SHA-256: + `c3d542dee7bfcc1901428e0c1408c0bd522d778d8e5e1dbca156cab008f3d712` +- Runtime edit source/destination SHA-256: + `5ac2f32f33724a2fc232d725c5c37d4cfe3e93b3c1f19aa4177d2c8e3009ec78` +- Runtime format: 44.1 kHz, 16-bit, stereo PCM WAV; 0.446780 seconds; + one-shot. + +#### Meow + +- Source page: https://freesound.org/s/495694/ +- Creator: Christyboy100 +- Source title: `Cat meow.m4a` +- License: Attribution 3.0 +- Downloaded source filename: `495694__christyboy100__cat-meow.m4a` +- Downloaded source SHA-256: + `afa3f6ef7afa2f7fee1e634d5505d2101cbba4421f8d8ff9adf562939c7a18f8` +- Converted working WAV SHA-256: + `a505e6e230e7d9d76b65c46270d97a35a1acc510b43aa5e7343d7b04f424cade` +- Runtime edit source/destination SHA-256: + `cce22ce1fbbc9325126a153aedc4526fde1aacf78780574fb432197dd44eb6b3` +- Runtime format: 44.1 kHz, 16-bit, stereo PCM WAV; 0.605420 seconds; + one-shot. +- The downloaded sources and working edits remain in the project owner's + source-work archive; only the finished runtime sounds ship in the game. + +### Rain ambience source record + +- Source page: https://freesound.org/s/212799/ +- Creator: Ayton +- Source title: `Rain Loop Ontario` +- License: Creative Commons Attribution 3.0 +- Downloaded source filename: `212799__ayton__rain-loop-ontario.aiff` +- Downloaded source SHA-256: + `b491fc036b78a274d9ac7f6acbd014af1b5d24f5cba9bd7e3fdc05c5432ee66f` +- Runtime edit SHA-256: + `b1979609b20fe34ed6f8902f3de65ae787a09abd71429e8aab850e87b74a4fa2` +- Changes: converted from AIFF to Ogg Vorbis and configured as looping + weather ambience. + +## Generated resources + +Godot `.import` sidecars and deterministic shoreline `.tres` meshes are derived +repository resources, not original artwork. `.godot/imported/`, editor caches, +captures, exports, and temporary test data are not tracked. + +## Release gate + +Before a public binary or source release: + +1. Search exported resources for external filesystem paths. +2. Include or make readily accessible the complete GPL text and identify the + exact corresponding source tag or commit. +3. Include `ASSET-LICENSE.md`, `TRADEMARKS.md`, this attribution/provenance + record, both font notices, and every applicable third-party license. +4. Retain the complete Godot Engine MIT notice above. +5. Preserve third-party terms without adding incompatible restrictions. +6. Avoid referring to a dirty or unavailable source revision. +7. Publish or offer equivalent access to exact corresponding GPL source for + every distributed binary release. + +Desktop and PortMaster build scripts stage these authoritative files as +sidecars. Godot export presets include them in exported resource packs for +platforms where sidecar documents are not normally visible. Store and release +pages should link to the exact source revision and public notices. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..63d3b08 --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,352 @@ +# Development reference + +This is the authoritative reference for NETfishing architecture, engineering +decisions, content policy, authoring, and validation. + +## Architecture + +### Composition + +`main/main.tscn` is the application composition root. It owns long-lived +services, the active world, player container, save/settings managers, and the +pixelated UI viewport. Services are scene-owned rather than global autoloads, +which makes their dependencies visible in the main scene and allows focused +tests to instantiate the same composition. + +The main script coordinates title, session, world, and UI lifecycle. Domain +logic remains in typed resources and services rather than being stored solely +in controls. + +### Domain boundaries + +- `fish/` defines species, availability, catches, pools, quality, and selection. +- `fishing/` owns cast/chase state and resolves authoritative fishing surfaces. +- `inventory/`, `items/`, `economy/`, `progression/`, and `jobs/` own persistent + player-facing game state. +- `world/` owns authored map composition and presentation. Water type is a + shared typed definition used by water bodies, fish habitat, fishing + validation, Logbook classification, and saltwater shoreline presentation. +- `ui/` observes and invokes domain services. It does not define stable item or + fish identity. + +Resource files (`.tres`) are authored data. Stable IDs, not display names or +node names, connect persistent and networked state to that data. + +### Networking and authority + +`NetworkSession` owns ENet session lifecycle and peer registration. Dedicated +network services validate and replicate bounded domains such as fishing, +sales, shops, item use, profiles, jobs, mail, chat, drawings, time, and weather. + +`DiscoveryClient` is an optional directory layer beside `NetworkSession`. An +open host may publish a short-lived room lease, and the shared Join Game page +may browse compatible leases before handing the selected address back to the +existing direct ENet connection flow. The directory does not carry gameplay +traffic or become a gameplay authority. Its base URL comes from +`network/discovery/base_url`, with `NETFISHING_DISCOVERY_URL` available as a +development/deployment override. + +The host is authoritative. Clients submit requests or evidence; the host +derives trusted context from registered peers, authoritative regions, and +server-owned state before mutating inventory, wallet, progression, or shared +world state. Private single-player uses the same host path. Client evidence is +bounded and reconstructed where possible; UI activation is never proof that a +server-side mutation succeeded. + +Moderation follows the same boundary. Player hosts may grant session-scoped +operator status to an authenticated identity. Dedicated servers derive +operators from their configured fingerprint allowlist. Operator status is +replicated for presentation, but kick, ban, unban, and artwork-reset requests +are always reauthorized against the authenticated sender by the host. Only a +player host can grant or revoke operators; operators cannot moderate the host +or another operator. + +Protocol compatibility is defined in `network/network_protocol.gd`. A visible +release version is not a reason to change the protocol number. + +### Persistence and stable identifiers + +`PlayerDataRoot` selects and validates a portable data root. Stores receive +paths from that owner rather than inventing unrelated locations. Progression is +written by `PlayerSaveManager`; device settings and social/identity stores have +separate formats and lifecycles. + +Save migrations are sequential and explicit. Existing catches and ownership +are keyed by stable IDs so authored metadata can evolve without rewriting +historical records. Current source constants—not documentation—are +authoritative for format versions. + +Repository-owned resources are authoritative authored data. Persistent and +networked records refer to stable IDs; display names, filenames, node names, +coordinates, and pool names are not compatibility identifiers. Cross-domain +concepts such as water type use one typed definition. Removing or changing a +stable ID requires an explicit compatibility plan. + +### Presentation + +Gameplay is rendered in 3D with the GL Compatibility renderer. The main UI is +presented through a uniformly scaled SubViewport. Shared UI components and +palette resources prevent page-specific geometry and style drift. + +World presentation systems (time/weather visuals, procedural sky and water, +shoreline ribbons, player blob shadows) do not own gameplay collision or +network authority. Generated shoreline meshes are deterministic presentation +resources baked from explicitly configured static terrain. + +### Engineering consequences + +- Shared mutations must behave consistently for private hosts, open hosts, + dedicated servers, and joined clients. +- Networking changes require local-host and paired host/client validation. +- Protocol payloads and rejection cleanup are explicit and bounded. +- Renaming visible UI does not require a save migration. +- Content validation covers stable-ID uniqueness, catalog completeness, pool + membership, and typed habitat compatibility. +- New authored assets require provenance records as well as resource + references. + +## Content and balance + +Balance is authored data and should change deliberately. Exact current values +remain authoritative in `.tres` resources and typed scripts. + +### Fish + +- Stable fish IDs and catalog numbers must not be changed for display cleanup. +- Species availability may constrain water type, time, weather, bait, and + other authored context. +- Location pools control selection weight; the global catalog remains + comprehensive. +- Freshwater and saltwater habitat is validated by the host in addition to + pool membership. +- Value, rarity, quality, weight ranges, and authored barrier-health bands + should be reviewed together because they affect economy and catch + difficulty. + +### Economy and progression + +- Sales and purchases are host-authoritative and must mutate inventory and + wallet exactly once. +- Reserved assets are rejected atomically; mixed valid and reserved batches + must not partially succeed. +- Item effects, shop prices, cooler capacity, fishing upgrades, jobs, and + experience are separate balance axes. Avoid changing several in an + unrelated presentation pass. + +Record affected stable IDs and resource paths, old and new values, intended +player-facing outcome, interactions with quality/availability/economy, +deterministic validation, and save-compatibility impact with each balance +change. Do not bump a save schema or network protocol merely because an +authored number changed. + +## Authoring + +### World composition + +The active test world uses the canonical starter island through +`world/regions/starter_island_region.tscn`. + +`world/test_world.tscn` owns the gameplay-wide environment, sun, world bounds, +and below-world failsafe. `StarterIslandRegion` owns the placed island content: + +```text +StarterIslandRegion +├── Terrain +│ ├── Visual +│ └── Collision +├── WaterBodies +│ ├── Pond +│ │ ├── VisualWater +│ │ ├── FishingRegion +│ │ └── RecoveryRegion +│ └── Ocean +│ ├── VisualWater +│ ├── FishingRegions +│ └── RecoveryRegions +├── PlayerSpawn +├── SafeRespawns +└── Interactables + ├── FishingShopWorld + └── PelicanCoolerPerch +``` + +Move a meaningful feature root rather than one of its implementation children. +Child transforms are local offsets owned by that feature. + +Every fishable region authors its `water_type` explicitly. The starter pond is +`FRESH_WATER`; the starter coast is `SALT_WATER`. Fish species use the same +central type through an allowed-water-type bitmask. Do not infer habitat from +node names, pool filenames, coordinates, or water height. + +Select `WaterBodies/Pond` to move or resize the pond. Its transform is the +authoritative surface position; surface size and fishing/recovery coverage are +owned together. Select `WaterBodies/Ocean` to move the surrounding water as one +feature. Its explicit visual, fishing, and recovery lobes remain local to that +root. + +Placed-feature ownership: + +- `PlayerSpawn` defines the initial player transform and facing. +- `SafeRespawns` contains authoritative recovery destinations. Recovery + preserves current facing. +- `Interactables/FishingShopWorld` owns the shop visual, collision, interaction + area, prompt, and entrance marker. +- `Interactables/PelicanCoolerPerch` owns the Pelican landmark visual; selling + remains a Cooler action. +- `Terrain` owns imported visuals and generated collision. +- `BelowWorldFailsafe/Coverage` owns below-world recovery coverage. + +The starter-island GLB hierarchy is the terrain-collision authority. The +region rebuilds one concave collision shape from every imported +`MeshInstance3D` when it loads, so newly exported terrain and props participate +without a separate stale collision bake. Keep purely decorative meshes out of +that hierarchy if they should not block players. + +The active `Environment`, `Sun`, and starter-island grass material remain +Inspector-authored and are not recreated by runtime scripts. + +### Reusable world props + +Use one placed `WorldProp` root for a complete landmark: + +```text +WorldProp +├── Visual +└── Collision + └── CollisionShape3D +``` + +Put optional interaction areas, labels, and markers beneath the same root. +Instance imported GLB content under `Visual` and keep gameplay collision under +`Collision`. Author reusable assets at scale `(1, 1, 1)`. Uniform provisional +root scaling is acceptable when visual and collision scale together; avoid +non-uniform root scaling. Make mutable per-instance shapes, meshes, and +materials local to the scene. + +### Shoreline tide ribbons + +Maps using generated tide ribbons own a `ShorelineRibbonBaker` and one +`ShorelineRibbonConfig` per water body. Each configuration selects only its +static terrain source, water height, generation bounds, water-facing reference, +smoothing overrides, and generated `.tres` path. Props, players, bobbers, and +gameplay areas are not scanned unless selected explicitly. Only `SALT_WATER` +bodies generate ribbons. + +The editor automatically rebuilds configured shoreline resources when an +authored terrain GLB is reimported. Select the baker and press **Rebuild +Shoreline Ribbons** only when intentionally forcing a rebuild without a terrain +reimport. The equivalent headless fallback and validation command is: + +```sh +godot --headless --path . --script scripts/bake_shoreline_ribbons.gd +``` + +Use `debug_path_stage` only while comparing extraction stages and leave it Off +in committed scenes. A rebuild is required when saltwater terrain at the +waterline, water height, or generation bounds change; terrain reimports handle +the usual case automatically. Normal gameplay loads committed generated meshes +and never runs extraction. + +### Facial-feature textures + +Put new PNGs under the matching directory in +`art/exported/characters/faces/`: + +```text +eyes/.png +noses/.png +mouth/.png +``` + +The category directory is authoritative. A category prefix is optional, so +both `sleepy.png` and `eyes_sleepy.png` produce the stable option ID `sleepy`. +Filenames normalize to lowercase snake_case and are stored in appearance +snapshots, so do not casually rename them. Preserve the established RGBA +transparent canvas. Restart a development build after adding an image; rebuild +exports to package new `res://` files. + +### Texture sampling + +NETfishing artwork always uses nearest-neighbor sampling. Do not enable linear, +bilinear, trilinear, or anisotropic texture filtering, and do not generate +mipmaps. Shader samplers must declare `filter_nearest`. The +`TextureSamplingPolicy` autoload applies nearest sampling to 2D canvas items, +Sprite3D presentations, imported 3D materials, and dynamically constructed +mesh materials at runtime. + +After importing new artwork, normalize its tracked import metadata where +needed and run the focused policy check: + +```sh +godot --headless --path . --script scripts/normalize_texture_imports.gd \ + -- --apply --root res://path/to/new/artwork +godot --headless --path . --script tests/texture_sampling_validation.gd +``` + +### Bubble menus + +Instance `ui/components/bubble_menu/bubble_button.tscn` for each action, or +attach `bubble_button.gd` to an existing button. Author neutral size, desktop +and compact anchors, font-size limits, and deterministic motion in the +Inspector. A label child may be assigned with `label_control_path`. + +Place buttons under a Control using `bubble_cluster.gd`, pass ordered button +references to `configure()`, and call `apply_layout()` when available size or +responsive layout changes. Order defines keyboard/controller focus neighbors. +The shared profile owns palette, styles, proportional-font ratio, hover +response, and contact tuning. Parent menus own labels, actions, anchors, +availability, and confirmation behavior. + +`motion_scale = 0.0` disables idle drift/deformation while preserving hover and +focus feedback. Contact correction is deterministic and bounded; do not replace +it with physics that destabilizes layout, focus order, or hit targets. + +## Validation + +Tests are executable Godot `SceneTree` scripts. Some are pure content/domain +checks, some instantiate the main scene, and multiplayer checks run paired host +and client processes on loopback. The repository runner provides isolation and +consistent entry points. + +### Consolidated runner + +```sh +scripts/run_validations.sh quick +scripts/run_validations.sh full +scripts/run_validations.sh host +scripts/run_validations.sh network +scripts/run_validations.sh all +scripts/run_validations.sh --list +``` + +- `quick` runs deterministic content and domain checks. +- `full` adds socket-free scene/runtime checks. +- `host` runs single-process authority checks that bind local UDP. +- `network` runs loopback host/client pairs. +- `all` runs `full`, `host`, and `network`. + +Set `GODOT_BIN` to select an executable and `TEST_TIMEOUT_SECONDS` to change +the per-process timeout. The runner isolates and removes XDG roots. + +For one focused test: + +```sh +test_root="$(mktemp -d)" +XDG_DATA_HOME="$test_root/data" \ +XDG_CONFIG_HOME="$test_root/config" \ +godot --headless --path . --script tests/fish_catalog_content_validation.gd +rm -rf -- "$test_root" +``` + +Do not point `NETFISHING_DATA_DIR` at an arbitrary empty directory; it is an +explicit portable-data override and must identify a valid data root. + +Headless tests cannot prove visual alignment, mouse routing, shader appearance, +controller feel, or resize behavior. Presentation changes require graphical +review at the canonical 1280×720 layout and relevant low/high and ultrawide +resolutions. + +Network tests require free loopback UDP ports and local-socket permission. A +release audit additionally requires clean Git/tag verification, editor import, +platform exports, executable smoke tests, archive inspection, and SHA-256 +verification; those checks are intentionally outside the development runner. diff --git a/docs/PORTMASTER.md b/docs/PORTMASTER.md new file mode 100644 index 0000000..2cd6038 --- /dev/null +++ b/docs/PORTMASTER.md @@ -0,0 +1,169 @@ +# PortMaster reference + +NETfishing's PortMaster release is a canonical PortMaster package, not a +version-named generic ZIP. The installer-facing archive must be named +`netfishing.zip`, and its root must contain exactly: + +```text +NETfishing.sh +netfishing/ +port.json +``` + +Do not derive a release package from an older local `portmaster-stage` +directory. The templates in `scripts/portmaster/` are authoritative. + +## Release contract + +- `port.json` uses schema version 4 and names `netfishing.zip`. +- `NETfishing.sh` starts with `# PORTMASTER: netfishing.zip, NETfishing.sh`. +- The executable is `netfishing/NETfishing.aarch64`. +- The package declares AArch64, two analog sticks, GLIBC 2.28, and + `weston_pkg_0.2.squashfs`. +- The launcher must not start GPTOKEYB. Godot and NETfishing's controller + mapping manager handle controller input directly. +- Persistent device data remains under `netfishing/conf/data`, + `netfishing/conf/config`, and `netfishing/conf/cache`. +- The archive must not contain `conf/`, saves, identities, logs, source files, + `.git`, or `.godot` content. +- Release downloads must publish `netfishing.zip`. A renamed versioned ZIP is + not a substitute because HarbourMaster identifies the canonical port by + archive name. + +## Build + +Run this only after the release commit and annotated `v` tag +are pinned to the same clean `main` commit: + +```bash +bash scripts/build_portmaster.sh +``` + +The script deletes and regenerates the Linux ARM64 export, stages the package, +validates its structure, and writes: + +```text +builds/v/netfishing.zip +``` + +`--package-only` is reserved for repackaging an already validated ARM64 export. +It does not rebuild game content. + +## Local muOS installation + +The muOS auto-install directory is: + +```text +/mnt/mmc/MUOS/PortMaster/autoinstall/ +``` + +For an explicit offline installation test, copy `netfishing.zip` to a temporary +device directory and run: + +```bash +/mnt/mmc/MUOS/PortMaster/harbourmaster \ + --offline --no-check install ./netfishing.zip +``` + +Before upgrading an existing installation, preserve +`/mnt/mmc/ports/netfishing/conf/` on the device. After installation: + +1. Confirm the installed executable and PCK hashes match the staged release. +2. Confirm the installed launcher contains the canonical PortMaster header and + does not contain `GPTOKEYB`. +3. Confirm `conf/` was not replaced or removed. +4. Launch the installed port through the normal muOS menu. +5. Verify the displayed game version and controller face-button mapping. + +Installing the public PortMaster catalog entry can still install an older +catalog build until the upstream `netfishing.zip` is updated. A local release +test must install the generated local archive explicitly. + +## Template provenance + +The current catalog screenshot is a 640x480 PNG copied +byte-for-byte into the package template: + +```text +f24daae48f543b3a270a5aa46b5e73a31904bf0c7216d4c8a9e15a8dc48a5eed screenshot.png +``` + +The remaining catalog metadata assets were copied from the installed official +`netfishing.zip` package and are intentionally preserved byte-for-byte: + +```text +e2a9d132744684c67865c01eca027a8c9946b4c5a19da57c82f1af0373dc83b7 gameinfo.xml +``` + +The package’s `licenses/` directory is assembled from the authoritative root +licenses and trademarks, the consolidated `docs/ATTRIBUTION.md`, and the font +notices at build time. It also records the exact release tag and corresponding +GPL source commit. Do not restore stale duplicated notice templates beneath +`scripts/portmaster/`. + +The original catalog porter credit, `Voyager`, remains in `port.json`. + +## muOS H700 controller mapping + +Godot 4.7.1 and the SDL2 utilities shipped by muOS identify the same virtual +controller differently. The PortMaster SDL2 database uses GUID +`19000000010000000100000000010000`, while Godot reports GUID +`19004ca6010000000100000000010000`. Passing the SDL2 entry unchanged leaves +most controls unmapped in Godot. + +The device-verified Godot mapping is: + +- A `b0`, B `b1`, Y `b2`, X `b3` +- left bumper `b4`, right bumper `b5` +- select `b6`, start `b7`, guide `b8` +- left-stick click `b9`, left trigger `b10`, right trigger `b11`, right-stick click `b12` +- D-pad `h0`; left and right sticks `a0..a3` + +`scripts/portmaster/NETfishing.sh` must replace the incompatible SDL2 entry +with the Godot entry when that SDL2 GUID is selected. Do not append the two +entries with a newline: WestonPack evaluates launcher arguments through a +shell and treats the second line as a command. Pass the single selected mapping +as `SDL_GAMECONTROLLERCONFIG` in the game command after Weston initializes; +Weston sources PortMaster's control file internally and otherwise restores the +SDL2 value. + +Do not call `Input.add_joy_mapping(..., true)` for a recognized connected muOS +controller. Updating this virtual controller after connection can stop Godot +from delivering its standardized controller events until restart. + +Before shipping a PortMaster build on H700 hardware, verify all of the +following in the installed game: + +- A, B, X, and Y each register independently. +- The D-pad navigates every menu direction. +- Both sticks, both bumpers, both triggers, Select, Start, L3, and R3 register. +- A single button never cancels auto-map. +- Holding both bumpers together for 1.25 seconds cancels auto-map. + +## Low-end performance profile + +The launcher keeps the normal game profile on unknown and stronger hardware. +It enables the low-end profile only when the Linux device tree reports one of +the identifiers used by Allwinner H616/H700 XX handhelds: + +- `allwinner,h616` +- `sun50iw9p1` +- `allwinner,sun50i-h700` + +The low-end profile passes these Godot options: + +- `--single-window` +- `--disable-vsync` +- `--max-fps 30` +- `--audio-output-latency 40` + +It also passes `NETFISHING_LOW_END=1`. The game renders the 3D world at 75% +linear resolution with nearest-neighbor scaling, reducing 3D pixel work by +about 44% while the separately rendered UI retains its canonical resolution. + +Do not use Godot's low processor mode for this profile. It reduces idle CPU +usage by sleeping between updates and is not a game-performance optimization. + +Before adding a device family, record its exact NUL-separated device-tree +`compatible` value from hardware. Do not infer detection from a retail product +name alone. diff --git a/playtest/README-PLAYTEST.txt b/docs/README-PLAYTEST.txt similarity index 80% rename from playtest/README-PLAYTEST.txt rename to docs/README-PLAYTEST.txt index 3b00680..9d895f1 100644 --- a/playtest/README-PLAYTEST.txt +++ b/docs/README-PLAYTEST.txt @@ -1,12 +1,13 @@ NETfishing -v0.3.5-prealpha -Pre-Alpha 0.3.5 +v0.7.1-alpha +Alpha 0.7.1 Thank you for trying this early private playtest. -NETfishing is currently a local gameplay prototype for a future -multiplayer-first game. Networking and multiplayer are NOT implemented in -this build. +NETfishing is an early multiplayer fishing game. This alpha includes local +progression, player-hosted and dedicated multiplayer sessions, public room +discovery, and direct UDP connections. Expect rough edges and please keep a +backup of any save or identity data you care about. INSTALLATION @@ -54,12 +55,15 @@ FISHING - Coffee improves movement; other supplies temporarily improve fishing. - The Cooler starts at 12 fish; buy permanent capacity expansions at the shop. - Reel Speed upgrades increase authoritative green reeling progress. -- Barrier Power upgrades increase damage per valid barrier action. +- Rod Power upgrades increase damage per valid barrier action. - Shop upgrades persist in the local progression save. - Escape closes shop confirmations before closing the shop itself. FEATURES TO TRY +- Hosting a private or public room and joining compatible online sessions +- Direct-IP, saved-server, recent-server, and public-room connection flows +- Text chat, player calls, player lists, relationships, and moderation tools - Movement, jumping, camera rotation, and zoom - Short, medium, and maximum-distance casts - Valid water and invalid land casts @@ -101,11 +105,11 @@ Please include: - How movement, casting, chase pressure, barrier clicking, menus, and camera controls felt -KNOWN PRE-ALPHA LIMITATIONS +KNOWN ALPHA LIMITATIONS -- Multiplayer/networking is not implemented. -- Fish and character art are temporary. -- UI and balance values are placeholders. -- There is no audio. +- Public sessions remain direct connections; restrictive NAT or carrier-grade + networks may prevent some players from joining. +- Multiplayer, dedicated-server deployment, UI, and balance are still alpha. +- Some fish and character artwork remains temporary. - There is no installer or automatic updater. - Windows builds are unsigned. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..49645b3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,60 @@ +# NETfishing documentation + +This directory is the single home for maintained project documentation. +Start here instead of searching feature, asset, world, or script directories +for local README files. + +## Authoritative references + +- [`DEVELOPMENT.md`](DEVELOPMENT.md): architecture, engineering decisions, + content and balance policy, authoring guidance, and validation. +- [`ATTRIBUTION.md`](ATTRIBUTION.md): ownership, credits, licensing map, + third-party notices, asset provenance, and redistribution requirements. +- [`PORTMASTER.md`](PORTMASTER.md): PortMaster packaging, installation, + controller mapping, performance behavior, and hardware release checks. +- [`README-PLAYTEST.txt`](README-PLAYTEST.txt): the player-facing README staged + into desktop playtest packages. + +## Related repositories + +- [Discovery service API](https://forge.makearmy.io/woofmeow/netfishing-discovery-server/src/branch/main/docs/API.md) + stays with the service implementation and defines its versioned HTTP + contract. +- [Dedicated-server packaging](https://forge.makearmy.io/woofmeow/netfishing-dedicated-server/src/branch/main/README.md) + stays with its installer and update scripts. It consumes this repository's + attribution and legal notices from the exact pinned game commit instead of + maintaining copies. + +Those code-coupled references remain in their own repositories. Game-wide +architecture, authoring, licensing, credits, and provenance are not duplicated +there. + +Exact runtime values and behavior remain authoritative in source code, scenes, +and resources. These documents describe boundaries, policy, and maintenance +procedures; they should not duplicate changing constants or inventories that +can be derived reliably from the project. + +## Files intentionally kept at the repository root + +The following are entry points or governing legal documents rather than +general project documentation, so they remain at conventional root paths: + +- `README.md` +- `CONTRIBUTING.md` +- `LICENSE` +- `ASSET-LICENSE.md` +- `TRADEMARKS.md` +- `CONTRIBUTOR-TERMS.md` + +Third-party font license texts stay beside their font files under `ui/fonts/` +so the licensed files and their original notices cannot drift apart. Build +scripts collect those legal files and the authoritative attribution record +when assembling releases. + +## Maintenance rule + +Do not add feature-specific README files elsewhere in the repository. Add or +update the appropriate reference above and link to its heading when source +comments need more context. Player-facing package instructions belong only in +`README-PLAYTEST.txt`; source and scripts remain authoritative for command-line +usage. diff --git a/drawing/surface_drawing_canvas.gd b/drawing/surface_drawing_canvas.gd index 2d4f506..f6ceb94 100644 --- a/drawing/surface_drawing_canvas.gd +++ b/drawing/surface_drawing_canvas.gd @@ -340,13 +340,13 @@ func _sample_cell_transform( tangent = Vector3.RIGHT var bitangent: Vector3 = normal.cross(tangent).normalized() var pixel_size: float = cell_size * clampf(fill, 0.05, 1.0) - var basis := Basis( + var cell_basis := Basis( tangent * pixel_size, bitangent * pixel_size, normal, ) return Transform3D( - basis, + cell_basis, to_local(point + normal * _surface_offset()), ) diff --git a/economy/buyers/pelicans.tres b/economy/buyers/pelicans.tres index 7dce4ec..f1a4807 100644 --- a/economy/buyers/pelicans.tres +++ b/economy/buyers/pelicans.tres @@ -9,4 +9,4 @@ display_name = "pelicans" animal_name_singular = "pelican" animal_name_plural = "pelicans" payout_multiplier = 0.25 -sale_message_template = "You sold your {fish} to the pelicans for ${payout}." +sale_message_template = "You sold your {fish} to the pelicans." diff --git a/economy/fish_buyer_profile.gd b/economy/fish_buyer_profile.gd index 485df20..9bb59de 100644 --- a/economy/fish_buyer_profile.gd +++ b/economy/fish_buyer_profile.gd @@ -53,8 +53,7 @@ func get_sale_message( var buyer_name: String = animal_name_plural if buyer_name.is_empty(): buyer_name = display_name - return "you sold your %s to the %s for $%d." % [ + return "you sold your %s to the %s." % [ fish_name, buyer_name, - payout, ] diff --git a/economy/fish_sale_service.gd b/economy/fish_sale_service.gd index 722f766..6aca45d 100644 --- a/economy/fish_sale_service.gd +++ b/economy/fish_sale_service.gd @@ -165,9 +165,10 @@ func _validate_batch( result.payout ) else: - result.sale_message = "you sold %d fish to the %s for $%d." % [ + result.sale_message = ( + "you sold %d fish to the %s." % [ result.fish_count, result.buyer_animal_name, - result.payout, - ] + ] + ) return result diff --git a/economy/fishing_shop_stock.gd b/economy/fishing_shop_stock.gd index 3d5e867..87795ad 100644 --- a/economy/fishing_shop_stock.gd +++ b/economy/fishing_shop_stock.gd @@ -5,18 +5,47 @@ const ItemCatalogType = preload("res://items/item_catalog.gd") const ItemDataType = preload("res://items/item_data.gd") const PlayerBagType = preload("res://inventory/player_bag.gd") const PlayerWalletType = preload("res://economy/player_wallet.gd") +const WORM_MAX_STACK: int = 10 +const FISH_FINDER_ID: StringName = &"fish_finder" +const BATTERIES_ID: StringName = &"batteries" const ITEM_PRICES: Dictionary[StringName, int] = { + &"worms": 1, + &"snails": 3, + &"shrimp": 7, + &"squid_chunks": 15, + &"whole_anchovy": 60, + &"whole_sardine": 30, + &"luminous_roe": 125, + &"the_standby": 750, &"coffee": 20, &"energy_drink": 35, &"snack": 30, - &"fish_finder": 60, + FISH_FINDER_ID: 500, + BATTERIES_ID: 15, +} +const BAIT_UNLOCK_PRICES: Dictionary[StringName, int] = { + &"snails": 400, + &"shrimp": 1200, + &"squid_chunks": 3500, + &"whole_anchovy": 22000, + &"whole_sardine": 9000, + &"luminous_roe": 50000, } const ITEM_ORDER: Array[StringName] = [ + &"worms", + &"snails", + &"shrimp", + &"squid_chunks", + &"whole_sardine", + &"whole_anchovy", + &"luminous_roe", + &"the_standby", + FISH_FINDER_ID, &"coffee", &"energy_drink", &"snack", - &"fish_finder", + BATTERIES_ID, ] @@ -28,6 +57,51 @@ static func get_stock_item_ids() -> Array[StringName]: return ITEM_ORDER.duplicate() +static func get_unlock_price(item_id: StringName) -> int: + return BAIT_UNLOCK_PRICES.get(item_id, -1) + + +static func get_purchase_quantity( + item_id: StringName, + owned: int, + maximum_stack: int = WORM_MAX_STACK, +) -> int: + if is_bait_topoff(item_id): + return maxi(maximum_stack - owned, 0) + return 1 + + +static func is_bait_topoff(item_id: StringName) -> bool: + return item_id == &"worms" or BAIT_UNLOCK_PRICES.has(item_id) + + +static func is_permanent_unlock( + item_id: StringName, + item: ItemDataType, +) -> bool: + return ( + item != null + and (item.is_lure() or item_id == FISH_FINDER_ID) + ) + + +static func is_passive_supply(item_id: StringName) -> bool: + return item_id == BATTERIES_ID + + +static func get_purchase_cost( + item_id: StringName, + quantity: int, + bait_unlocked: bool, +) -> int: + if quantity <= 0: + return -1 + if is_bait_topoff(item_id) and item_id != &"worms" and not bait_unlocked: + return get_unlock_price(item_id) + var unit_price: int = get_price(item_id) + return unit_price * quantity if unit_price >= 0 else -1 + + static func purchase_one( item_id: StringName, wallet: PlayerWalletType, @@ -36,22 +110,42 @@ static func purchase_one( ) -> bool: if wallet == null or bag == null or catalog == null: return false - var price: int = get_price(item_id) var item: ItemDataType = catalog.get_item_by_id(item_id) + var bait_topoff: bool = is_bait_topoff(item_id) + var permanent_unlock: bool = is_permanent_unlock(item_id, item) + var quantity: int = get_purchase_quantity( + item_id, + bag.get_quantity(item_id), + item.max_stack if item != null else WORM_MAX_STACK, + ) + var price: int = get_purchase_cost( + item_id, + quantity, + bag.is_bait_unlocked(item_id), + ) if ( price < 0 or item == null or not item.is_valid() - or item.category != ItemDataType.Category.CONSUMABLE - or not item.stackable - or not item.usable - or not bag.can_add_item(item_id, 1) + or ( + item.category != ItemDataType.Category.CONSUMABLE + and not bait_topoff + and not permanent_unlock + ) + or (not item.stackable and not permanent_unlock) + or ( + not item.usable + and not bait_topoff + and not permanent_unlock + and not is_passive_supply(item_id) + ) + or not bag.can_add_item(item_id, quantity) or not wallet.can_afford(price) ): return false if not wallet.debit(price): return false - if bag.add_item(item_id, 1): + if bag.add_item(item_id, quantity): return true if not wallet.credit(price): push_error("Fishing Shop failed to roll back an item purchase.") diff --git a/export_presets.cfg b/export_presets.cfg index b6c7593..bbcdec2 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -7,9 +7,9 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="" -exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.3.5-prealpha/windows-x86_64/NETfishing.exe" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" +export_path="builds/v0.7.1-alpha/windows-x86_64/NETfishing.exe" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -32,16 +32,16 @@ codesign/digest_algorithm=1 codesign/description="" codesign/custom_options=PackedStringArray() application/modify_resources=true -application/icon="" +application/icon="res://art/exported/system_icons/netfishing.ico" application/console_wrapper_icon="" application/icon_interpolation=4 -application/file_version="0.3.5.0" -application/product_version="0.3.5.0" -application/company_name="" +application/file_version="0.7.1.0" +application/product_version="0.7.1.0" +application/company_name="Woofmeow" application/product_name="NETfishing" -application/file_description="NETfishing v0.3.5-prealpha" -application/copyright="" -application/trademarks="" +application/file_description="NETfishing v0.7.1-alpha" +application/copyright="Copyright © 2026 Woofmeow" +application/trademarks="NETfishing and Woofmeow branding is reserved; see TRADEMARKS.md" application/export_angle=0 ssh_remote_deploy/enabled=false ssh_remote_deploy/host="user@host_ip" @@ -51,6 +51,158 @@ ssh_remote_deploy/extra_args_scp="" ssh_remote_deploy/run_script="#!/usr/bin/env bash\nexport DISPLAY=:0\nunzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n\"{temp_dir}/{exe_name}\" {cmd_args}" ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash\nkill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\nrm -rf \"{temp_dir}\"" +[preset.3] + +name="Linux ARM64" +platform="Linux" +runnable=false +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" +export_path="builds/v0.7.1-alpha/linux-arm64/NETfishing.arm64" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.3.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=0 +binary_format/embed_pck=false +binary_format/architecture="arm64" +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false + +[preset.4] + +name="macOS" +platform="macOS" +runnable=false +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" +export_path="builds/v0.7.1-alpha/macos/NETfishing.zip" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.4.options] + +custom_template/debug="" +custom_template/release="" +application/bundle_identifier="io.woofmeow.netfishing" +application/icon="res://art/exported/system_icons/netfishing_1024.png" +application/icon_interpolation=0 +application/short_version="0.7.1" +application/version="0.7.1" +application/architecture="universal" +codesign/enable=false +notarization/enable=false + +[preset.5] + +name="Linux Dedicated Server" +platform="Linux" +runnable=false +advanced_options=false +dedicated_server=true +custom_features="" +export_filter="all_resources" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*,tests/*" +export_path="builds/v0.7.1-alpha/server-linux-x86_64/NETfishingServer.x86_64" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.5.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=0 +binary_format/embed_pck=false +binary_format/architecture="x86_64" +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false + +[preset.2] + +name="Android" +platform="Android" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" +export_path="builds/v0.7.1-alpha/android/NETfishing.apk" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.2.options] + +custom_template/debug="" +custom_template/release="" +gradle_build/use_gradle_build=false +gradle_build/export_format=0 +gradle_build/min_sdk="" +gradle_build/target_sdk="" +architectures/armeabi-v7a=false +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=false +version/code=70100 +version/name="v0.7.1-alpha" +package/unique_name="io.woofmeow.netfishing" +package/name="NETfishing" +package/signed=true +package/app_category=0 +package/retain_data_on_uninstall=false +package/exclude_from_recents=false +package/show_in_android_tv=false +package/show_in_app_library=true +package/show_as_launcher_app=true +launcher_icons/main_192x192="res://art/exported/system_icons/netfishing_256.png" +graphics/opengl_debug=false +screen/immersive_mode=true +screen/edge_to_edge=false +screen/support_small=false +screen/support_normal=true +screen/support_large=true +screen/support_xlarge=true +user_data_backup/allow=false +gesture/swipe_to_dismiss=false +permissions/internet=true +permissions/access_network_state=true +permissions/custom_permissions=PackedStringArray() +texture_format/s3tc_bptc=false +texture_format/etc2_astc=true + [preset.1] name="Linux Desktop" @@ -60,9 +212,9 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="" -exclude_filter="builds/*,playtest/*,scripts/*" -export_path="builds/v0.3.5-prealpha/linux-x86_64/NETfishing.x86_64" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" +export_path="builds/v0.7.1-alpha/linux-x86_64/NETfishing.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" diff --git a/fish/fish_catch.gd b/fish/fish_catch.gd index 5edc9c4..a0d3738 100644 --- a/fish/fish_catch.gd +++ b/fish/fish_catch.gd @@ -127,7 +127,7 @@ static func from_save_dict( 0.0, MAX_SAFE_WEIGHT_LB ) - var loaded_scale: float = _read_safe_float( + var _loaded_scale: float = _read_safe_float( data["display_scale"], 0.0, MAX_SAFE_DISPLAY_SCALE @@ -141,7 +141,7 @@ static func from_save_dict( or loaded_sale_value < 0 or not FishQualityType.is_valid(loaded_quality) or loaded_weight <= 0.0 - or loaded_scale <= 0.0 + or _loaded_scale <= 0.0 or typeof(loaded_favorite) != TYPE_BOOL ): return null @@ -152,7 +152,12 @@ static func from_save_dict( fish_catch.catch_id = loaded_catch_id fish_catch.catch_sequence = loaded_sequence fish_catch.weight_lb = loaded_weight - fish_catch.display_scale = loaded_scale + # Recompute presentation size from authoritative weight. This keeps saves + # written before the weight-based sizing rule from retaining oversized + # per-species display values. + fish_catch.display_scale = resolved_fish.get_display_scale_for_weight( + loaded_weight + ) fish_catch.quality = loaded_quality fish_catch.sale_value = loaded_sale_value fish_catch.is_favorited = bool(loaded_favorite) @@ -180,7 +185,7 @@ static func from_network_dict( var loaded_weight: float = _read_safe_float( data.get("weight_lb"), 0.0, MAX_SAFE_WEIGHT_LB ) - var loaded_scale: float = _read_safe_float( + var _loaded_scale: float = _read_safe_float( data.get("display_scale"), 0.0, MAX_SAFE_DISPLAY_SCALE ) var loaded_value: int = _read_safe_integer( @@ -196,7 +201,7 @@ static func from_network_dict( or loaded_id.length() > 160 or loaded_fish_id != resolved_fish.id or loaded_weight <= 0.0 - or loaded_scale <= 0.0 + or _loaded_scale <= 0.0 or loaded_value < 0 or not FishQualityType.is_valid(loaded_quality) ): @@ -207,7 +212,9 @@ static func from_network_dict( fish_catch.catch_id = loaded_id fish_catch.catch_sequence = 0 fish_catch.weight_lb = loaded_weight - fish_catch.display_scale = loaded_scale + fish_catch.display_scale = resolved_fish.get_display_scale_for_weight( + loaded_weight + ) fish_catch.quality = loaded_quality fish_catch.sale_value = loaded_value fish_catch.is_favorited = false diff --git a/fish/fish_data.gd b/fish/fish_data.gd index fd327b1..2bb99af 100644 --- a/fish/fish_data.gd +++ b/fish/fish_data.gd @@ -1,6 +1,16 @@ class_name FishData extends Resource +# Fish are displayed by their physical weight, rather than by per-species +# texture or resource dimensions. A cube-root curve approximates the way a +# fish's linear dimensions grow with mass while keeping large catches readable +# beside the player. +const DISPLAY_REFERENCE_WEIGHT_LB: float = 1.0 +const DISPLAY_REFERENCE_SCALE: float = 0.85 +const DISPLAY_WEIGHT_EXPONENT: float = 0.33333334 +const DISPLAY_MIN_SCALE: float = 0.45 +const DISPLAY_MAX_SCALE: float = 3.0 + const CatchDifficultyProfileType = preload( "res://fishing/catch_difficulty_profile.gd" ) @@ -24,6 +34,8 @@ var allowed_water_types: int = WaterType.ALL_FISHABLE_MASK @export var availability: FishAvailabilityType @export_range(0.01, 1000.0, 0.01) var weight_min_lb: float = 0.5 @export_range(0.01, 1000.0, 0.01) var weight_max_lb: float = 1.0 +# Retained in resource files for compatibility with existing authored data; +# runtime presentation is derived from absolute weight below. @export_range(0.01, 20.0, 0.01) var display_scale_min: float = 0.8 @export_range(0.01, 20.0, 0.01) var display_scale_max: float = 1.2 @export_range(0.01, 10.0, 0.01) var display_scale_curve: float = 1.0 @@ -69,23 +81,26 @@ func get_maximum_weight() -> float: return maxf(weight_max_lb, get_minimum_weight()) -func get_display_scale_for_weight(weight_lb: float) -> float: +func get_weight_percentile(weight_lb: float) -> float: var minimum_weight: float = get_minimum_weight() var maximum_weight: float = get_maximum_weight() - var normalized_weight: float = 0.0 - if maximum_weight > minimum_weight: - normalized_weight = inverse_lerp( - minimum_weight, - maximum_weight, - weight_lb - ) - normalized_weight = pow( - clampf(normalized_weight, 0.0, 1.0), - maxf(display_scale_curve, 0.01) + if maximum_weight <= minimum_weight: + return 0.0 + return clampf( + inverse_lerp(minimum_weight, maximum_weight, weight_lb), + 0.0, + 1.0, ) - var minimum_scale: float = maxf(display_scale_min, 0.01) - var maximum_scale: float = maxf(display_scale_max, minimum_scale) - return lerpf(minimum_scale, maximum_scale, normalized_weight) + + +func get_display_scale_for_weight(weight_lb: float) -> float: + var safe_weight_lb: float = maxf(weight_lb, 0.01) + var weight_ratio: float = safe_weight_lb / DISPLAY_REFERENCE_WEIGHT_LB + var weight_scale: float = DISPLAY_REFERENCE_SCALE * pow( + weight_ratio, + DISPLAY_WEIGHT_EXPONENT, + ) + return clampf(weight_scale, DISPLAY_MIN_SCALE, DISPLAY_MAX_SCALE) func get_sale_value_for_weight(weight_lb: float) -> int: diff --git a/fish/fish_quality.gd b/fish/fish_quality.gd index f851e66..61534e0 100644 --- a/fish/fish_quality.gd +++ b/fish/fish_quality.gd @@ -15,7 +15,21 @@ const ALL_TIERS_MASK: int = (1 << TIER_COUNT) - 1 # Starting distribution. Equipment and tackle can supply per-tier # multipliers later without changing catch serialization or tier identity. const BASE_ROLL_WEIGHTS: Array[float] = [40.0, 32.0, 18.0, 8.0, 2.0] +const WORM_ROLL_WEIGHTS: Array[float] = [80.0, 10.0, 5.0, 4.0, 1.0] +const SNAIL_ROLL_WEIGHTS: Array[float] = [68.0, 16.0, 8.0, 6.0, 2.0] +const SHRIMP_ROLL_WEIGHTS: Array[float] = [56.0, 21.0, 12.0, 8.0, 3.0] +const SQUID_ROLL_WEIGHTS: Array[float] = [44.0, 25.0, 16.0, 11.0, 4.0] +const MINNOW_ROLL_WEIGHTS: Array[float] = [34.0, 27.0, 20.0, 13.0, 6.0] +const SARDINE_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.0] +const LUMINOUS_ROE_ROLL_WEIGHTS: Array[float] = [16.0, 26.0, 26.0, 20.0, 12.0] const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0] +# Legacy multiplier helpers remain available for compatibility. New encounters +# roll directly within the authored quality bands below. +const BARRIER_HEALTH_MULTIPLIERS: Array[float] = [1.0, 1.25, 1.6, 2.2, 3.25] +# Inclusive per-barrier health bands authored for each quality tier. Encounter +# generation rolls uniformly across the selected tier's complete band. +const BARRIER_HEALTH_MINIMUMS: Array[int] = [1, 10, 50, 100, 200] +const BARRIER_HEALTH_MAXIMUMS: Array[int] = [9, 50, 100, 200, 400] const DISPLAY_NAMES: PackedStringArray = [ "boring", "average", @@ -52,6 +66,44 @@ static func apply_sale_value(base_value: int, quality: int) -> int: ) +static func barrier_health_multiplier(quality: int) -> float: + return ( + BARRIER_HEALTH_MULTIPLIERS[quality] + if is_valid(quality) + else BARRIER_HEALTH_MULTIPLIERS[Tier.BORING] + ) + + +static func apply_barrier_health(base_health: int, quality: int) -> int: + return maxi( + ceili( + float(maxi(base_health, 1)) + * barrier_health_multiplier(quality) + ), + 1, + ) + + +static func barrier_health_range(quality: int) -> Vector2i: + var safe_quality: int = ( + quality if is_valid(quality) else Tier.BORING + ) + return Vector2i( + BARRIER_HEALTH_MINIMUMS[safe_quality], + BARRIER_HEALTH_MAXIMUMS[safe_quality], + ) + + +static func roll_barrier_health( + rng: RandomNumberGenerator, + quality: int, +) -> int: + var health_range: Vector2i = barrier_health_range(quality) + if rng == null: + return health_range.x + return rng.randi_range(health_range.x, health_range.y) + + static func roll( rng: RandomNumberGenerator, weight_multipliers: Array[float] = [], @@ -78,6 +130,38 @@ static func roll( return Tier.SHINY +static func roll_weights_for_bait(active_bait_tags: Array[StringName]) -> Array[float]: + var bait_weights: Array[float] = rarity_weights_for_bait(active_bait_tags) + var weights: Array[float] = [] + for quality: int in TIER_COUNT: + weights.append(bait_weights[quality] / BASE_ROLL_WEIGHTS[quality]) + return weights + + +static func rarity_weights_for_bait( + active_bait_tags: Array[StringName], +) -> Array[float]: + var weights: Array[float] = [] + if active_bait_tags.is_empty(): + weights.assign([100.0, 0.0, 0.0, 0.0, 0.0]) + elif active_bait_tags.has(&"bait_rarity_6"): + weights.assign(LUMINOUS_ROE_ROLL_WEIGHTS) + elif active_bait_tags.has(&"bait_rarity_5"): + weights.assign(SARDINE_ROLL_WEIGHTS) + elif active_bait_tags.has(&"bait_rarity_4"): + weights.assign(MINNOW_ROLL_WEIGHTS) + elif active_bait_tags.has(&"bait_rarity_3"): + weights.assign(SQUID_ROLL_WEIGHTS) + elif active_bait_tags.has(&"bait_rarity_2"): + weights.assign(SHRIMP_ROLL_WEIGHTS) + elif active_bait_tags.has(&"bait_rarity_1"): + weights.assign(SNAIL_ROLL_WEIGHTS) + else: + # Unknown and legacy bait tags retain the original worms behavior. + weights.assign(WORM_ROLL_WEIGHTS) + return weights + + static func qualified_name(fish_name: String, quality: int) -> String: return "%s %s" % [display_name(quality), fish_name] diff --git a/fish/fish_selector.gd b/fish/fish_selector.gd index 8ed9a2a..506a906 100644 --- a/fish/fish_selector.gd +++ b/fish/fish_selector.gd @@ -14,6 +14,7 @@ var quality_weight_multipliers: Array[float] = [] var use_deterministic_test_seed: bool = false var deterministic_test_seed: int = 24680 var selection_seed: int = 0 +var _active_bait_tags: Array[StringName] = [] var _rng := RandomNumberGenerator.new() @@ -34,8 +35,12 @@ func select_fish( ) -> FishDataType: if pool == null or context == null or collection_log == null: return null - - var eligible_fish: Array[FishDataType] = [] + _active_bait_tags = context.active_bait_tags.duplicate() + var all_fish: Array[FishDataType] = [] + var all_weights: Array[float] = [] + var rarity_fish: Array[FishDataType] = [] + var rarity_weights: Array[float] = [] + var selected_rarity: int = _roll_rarity(context) var weights: Array[float] = [] var total_weight: float = 0.0 for fish: FishDataType in pool.candidates: @@ -46,17 +51,23 @@ func select_fish( if fish.availability != null and not fish.availability.is_available(context): continue var final_weight: float = fish.base_catch_weight - if fish.availability != null: - final_weight *= fish.availability.get_bait_weight_multiplier(context) + # Species-specific preferred bait is deprecated. Bait now controls + # quality and rarity bands globally; required tags remain authoritative. if not collection_log.has_discovered(fish.id): final_weight *= maxf(undiscovered_weight_multiplier, 0.0) if fish.rarity >= 0 and fish.rarity < rarity_weight_multipliers.size(): final_weight *= maxf(rarity_weight_multipliers[fish.rarity], 0.0) if final_weight <= 0.0: continue - eligible_fish.append(fish) - weights.append(final_weight) - total_weight += final_weight + all_fish.append(fish) + all_weights.append(final_weight) + if int(fish.rarity) == selected_rarity: + rarity_fish.append(fish) + rarity_weights.append(final_weight) + var eligible_fish: Array[FishDataType] = rarity_fish if not rarity_fish.is_empty() else all_fish + weights = rarity_weights if not rarity_weights.is_empty() else all_weights + for weight: float in weights: + total_weight += weight if eligible_fish.is_empty() or total_weight <= 0.0: return null @@ -69,7 +80,27 @@ func select_fish( return eligible_fish.back() -func create_catch(fish: FishDataType) -> FishCatchType: +func _roll_rarity(context: FishingContextType) -> int: + var weights: Array[float] = FishQualityType.rarity_weights_for_bait( + context.active_bait_tags + ) + for index: int in range(weights.size()): + if index < rarity_weight_multipliers.size(): + weights[index] *= maxf(rarity_weight_multipliers[index], 0.0) + var total: float = 0.0 + for weight: float in weights: + total += weight + if total <= 0.0: + return FishDataType.Rarity.COMMON + var roll: float = _rng.randf() * total + for index: int in range(weights.size()): + roll -= weights[index] + if roll <= 0.0: + return index + return FishDataType.Rarity.LEGENDARY + + +func create_catch(fish: FishDataType, bait_tags: Array[StringName] = []) -> FishCatchType: if fish == null or not fish.is_selectable(): return null var caught_fish := FishCatchType.new() @@ -83,9 +114,13 @@ func create_catch(fish: FishDataType) -> FishCatchType: caught_fish.display_scale = fish.get_display_scale_for_weight( caught_fish.weight_lb ) + var effective_bait: Array[StringName] = bait_tags if not bait_tags.is_empty() else _active_bait_tags + var quality_multipliers: Array[float] = FishQualityType.roll_weights_for_bait(effective_bait) + for quality: int in range(mini(quality_multipliers.size(), quality_weight_multipliers.size())): + quality_multipliers[quality] *= maxf(quality_weight_multipliers[quality], 0.0) caught_fish.quality = FishQualityType.roll( _rng, - quality_weight_multipliers, + quality_multipliers, ) caught_fish.sale_value = FishQualityType.apply_sale_value( fish.get_sale_value_for_weight(caught_fish.weight_lb), diff --git a/fish/pools/fish_catalog.tres b/fish/pools/fish_catalog.tres index 66a1c34..73609c8 100644 --- a/fish/pools/fish_catalog.tres +++ b/fish/pools/fish_catalog.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" load_steps=10 format=3] +[gd_resource type="Resource" load_steps=55 format=3] [ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"] [ext_resource type="Resource" path="res://fish/species/bluegill/bluegill.tres" id="2_bluegill"] @@ -9,7 +9,52 @@ [ext_resource type="Resource" path="res://fish/species/catfish_channel/catfish_channel.tres" id="7_catfish_channel"] [ext_resource type="Resource" path="res://fish/species/catfish_flathead/catfish_flathead.tres" id="8_catfish_flathead"] [ext_resource type="Resource" path="res://fish/species/catfish_white/catfish_white.tres" id="9_catfish_white"] +[ext_resource type="Resource" path="res://fish/species/tuna_albacore/tuna_albacore.tres" id="10_tuna_albacore"] +[ext_resource type="Resource" path="res://fish/species/tuna_bigeye/tuna_bigeye.tres" id="11_tuna_bigeye"] +[ext_resource type="Resource" path="res://fish/species/tuna_bluefin/tuna_bluefin.tres" id="12_tuna_bluefin"] +[ext_resource type="Resource" path="res://fish/species/tuna_skipjack/tuna_skipjack.tres" id="13_tuna_skipjack"] +[ext_resource type="Resource" path="res://fish/species/tuna_yellowfin/tuna_yellowfin.tres" id="14_tuna_yellowfin"] +[ext_resource type="Resource" path="res://fish/species/goby_round/goby_round.tres" id="15_goby_round"] +[ext_resource type="Resource" path="res://fish/species/salmon_atlantic/salmon_atlantic.tres" id="16_salmon_atlantic"] +[ext_resource type="Resource" path="res://fish/species/salmon_chum/salmon_chum.tres" id="17_salmon_chum"] +[ext_resource type="Resource" path="res://fish/species/salmon_coho/salmon_coho.tres" id="18_salmon_coho"] +[ext_resource type="Resource" path="res://fish/species/salmon_pink/salmon_pink.tres" id="19_salmon_pink"] +[ext_resource type="Resource" path="res://fish/species/salmon_sockeye/salmon_sockeye.tres" id="20_salmon_sockeye"] +[ext_resource type="Resource" path="res://fish/species/anchovy_european/anchovy_european.tres" id="21_anchovy_european"] +[ext_resource type="Resource" path="res://fish/species/anchovy_northern/anchovy_northern.tres" id="22_anchovy_northern"] +[ext_resource type="Resource" path="res://fish/species/chub_european/chub_european.tres" id="23_chub_european"] +[ext_resource type="Resource" path="res://fish/species/chub_flame/chub_flame.tres" id="24_chub_flame"] +[ext_resource type="Resource" path="res://fish/species/chub_lake/chub_lake.tres" id="25_chub_lake"] +[ext_resource type="Resource" path="res://fish/species/goldfish/goldfish.tres" id="26_goldfish"] +[ext_resource type="Resource" path="res://fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres" id="27_goldfish_bubbleeye"] +[ext_resource type="Resource" path="res://fish/species/grouper_gulf/grouper_gulf.tres" id="28_grouper_gulf"] +[ext_resource type="Resource" path="res://fish/species/grouper_red/grouper_red.tres" id="29_grouper_red"] +[ext_resource type="Resource" path="res://fish/species/mackerel_atlantic/mackerel_atlantic.tres" id="30_mackerel_atlantic"] +[ext_resource type="Resource" path="res://fish/species/mackerel_cero/mackerel_cero.tres" id="31_mackerel_cero"] +[ext_resource type="Resource" path="res://fish/species/mackerel_chub/mackerel_chub.tres" id="32_mackerel_chub"] +[ext_resource type="Resource" path="res://fish/species/mackerel_king/mackerel_king.tres" id="33_mackerel_king"] +[ext_resource type="Resource" path="res://fish/species/mackerel_spanish/mackerel_spanish.tres" id="34_mackerel_spanish"] +[ext_resource type="Resource" path="res://fish/species/marlin_black/marlin_black.tres" id="35_marlin_black"] +[ext_resource type="Resource" path="res://fish/species/marlin_blue/marlin_blue.tres" id="36_marlin_blue"] +[ext_resource type="Resource" path="res://fish/species/marlin_white/marlin_white.tres" id="37_marlin_white"] +[ext_resource type="Resource" path="res://fish/species/pomfret_black/pomfret_black.tres" id="38_pomfret_black"] +[ext_resource type="Resource" path="res://fish/species/pomfret_chinese/pomfret_chinese.tres" id="39_pomfret_chinese"] +[ext_resource type="Resource" path="res://fish/species/pomfret_golden/pomfret_golden.tres" id="40_pomfret_golden"] +[ext_resource type="Resource" path="res://fish/species/pomfret_white/pomfret_white.tres" id="41_pomfret_white"] +[ext_resource type="Resource" path="res://fish/species/sailfish/sailfish.tres" id="42_sailfish"] +[ext_resource type="Resource" path="res://fish/species/sauger/sauger.tres" id="43_sauger"] +[ext_resource type="Resource" path="res://fish/species/saugeye/saugeye.tres" id="44_saugeye"] +[ext_resource type="Resource" path="res://fish/species/snapper_lane/snapper_lane.tres" id="45_snapper_lane"] +[ext_resource type="Resource" path="res://fish/species/snapper_mangrove/snapper_mangrove.tres" id="46_snapper_mangrove"] +[ext_resource type="Resource" path="res://fish/species/snapper_mutton/snapper_mutton.tres" id="47_snapper_mutton"] +[ext_resource type="Resource" path="res://fish/species/snapper_red/snapper_red.tres" id="48_snapper_red"] +[ext_resource type="Resource" path="res://fish/species/swordfish/swordfish.tres" id="49_swordfish"] +[ext_resource type="Resource" path="res://fish/species/trout_cutthroat/trout_cutthroat.tres" id="50_trout_cutthroat"] +[ext_resource type="Resource" path="res://fish/species/trout_golden/trout_golden.tres" id="51_trout_golden"] +[ext_resource type="Resource" path="res://fish/species/trout_rainbow/trout_rainbow.tres" id="52_trout_rainbow"] +[ext_resource type="Resource" path="res://fish/species/trout_steelhead/trout_steelhead.tres" id="53_trout_steelhead"] +[ext_resource type="Resource" path="res://fish/species/walleye/walleye.tres" id="54_walleye"] [resource] script = ExtResource("1_pool") -candidates = [ExtResource("2_bluegill"), ExtResource("3_bass"), ExtResource("4_carp"), ExtResource("5_sunfish"), ExtResource("6_catfish_blue"), ExtResource("7_catfish_channel"), ExtResource("8_catfish_flathead"), ExtResource("9_catfish_white")] +candidates = [ExtResource("2_bluegill"), ExtResource("3_bass"), ExtResource("4_carp"), ExtResource("5_sunfish"), ExtResource("6_catfish_blue"), ExtResource("7_catfish_channel"), ExtResource("8_catfish_flathead"), ExtResource("9_catfish_white"), ExtResource("10_tuna_albacore"), ExtResource("11_tuna_bigeye"), ExtResource("12_tuna_bluefin"), ExtResource("13_tuna_skipjack"), ExtResource("14_tuna_yellowfin"), ExtResource("15_goby_round"), ExtResource("16_salmon_atlantic"), ExtResource("17_salmon_chum"), ExtResource("18_salmon_coho"), ExtResource("19_salmon_pink"), ExtResource("20_salmon_sockeye"), ExtResource("21_anchovy_european"), ExtResource("22_anchovy_northern"), ExtResource("23_chub_european"), ExtResource("24_chub_flame"), ExtResource("25_chub_lake"), ExtResource("26_goldfish"), ExtResource("27_goldfish_bubbleeye"), ExtResource("28_grouper_gulf"), ExtResource("29_grouper_red"), ExtResource("30_mackerel_atlantic"), ExtResource("31_mackerel_cero"), ExtResource("32_mackerel_chub"), ExtResource("33_mackerel_king"), ExtResource("34_mackerel_spanish"), ExtResource("35_marlin_black"), ExtResource("36_marlin_blue"), ExtResource("37_marlin_white"), ExtResource("38_pomfret_black"), ExtResource("39_pomfret_chinese"), ExtResource("40_pomfret_golden"), ExtResource("41_pomfret_white"), ExtResource("42_sailfish"), ExtResource("43_sauger"), ExtResource("44_saugeye"), ExtResource("45_snapper_lane"), ExtResource("46_snapper_mangrove"), ExtResource("47_snapper_mutton"), ExtResource("48_snapper_red"), ExtResource("49_swordfish"), ExtResource("50_trout_cutthroat"), ExtResource("51_trout_golden"), ExtResource("52_trout_rainbow"), ExtResource("53_trout_steelhead"), ExtResource("54_walleye")] diff --git a/fish/pools/starter_ocean_pool.tres b/fish/pools/starter_ocean_pool.tres index a84106e..1e47a2b 100644 --- a/fish/pools/starter_ocean_pool.tres +++ b/fish/pools/starter_ocean_pool.tres @@ -1,9 +1,41 @@ -[gd_resource type="Resource" load_steps=4 format=3] +[gd_resource type="Resource" load_steps=36 format=3] [ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"] [ext_resource type="Resource" path="res://fish/species/bass/bass.tres" id="2_bass"] [ext_resource type="Resource" path="res://fish/species/sunfish/sunfish.tres" id="3_sunfish"] +[ext_resource type="Resource" path="res://fish/species/tuna_albacore/tuna_albacore.tres" id="4_tuna_albacore"] +[ext_resource type="Resource" path="res://fish/species/tuna_bigeye/tuna_bigeye.tres" id="5_tuna_bigeye"] +[ext_resource type="Resource" path="res://fish/species/tuna_bluefin/tuna_bluefin.tres" id="6_tuna_bluefin"] +[ext_resource type="Resource" path="res://fish/species/tuna_skipjack/tuna_skipjack.tres" id="7_tuna_skipjack"] +[ext_resource type="Resource" path="res://fish/species/tuna_yellowfin/tuna_yellowfin.tres" id="8_tuna_yellowfin"] +[ext_resource type="Resource" path="res://fish/species/salmon_atlantic/salmon_atlantic.tres" id="9_salmon_atlantic"] +[ext_resource type="Resource" path="res://fish/species/salmon_chum/salmon_chum.tres" id="10_salmon_chum"] +[ext_resource type="Resource" path="res://fish/species/salmon_coho/salmon_coho.tres" id="11_salmon_coho"] +[ext_resource type="Resource" path="res://fish/species/salmon_pink/salmon_pink.tres" id="12_salmon_pink"] +[ext_resource type="Resource" path="res://fish/species/salmon_sockeye/salmon_sockeye.tres" id="13_salmon_sockeye"] +[ext_resource type="Resource" path="res://fish/species/anchovy_european/anchovy_european.tres" id="14_anchovy_european"] +[ext_resource type="Resource" path="res://fish/species/anchovy_northern/anchovy_northern.tres" id="15_anchovy_northern"] +[ext_resource type="Resource" path="res://fish/species/grouper_gulf/grouper_gulf.tres" id="16_grouper_gulf"] +[ext_resource type="Resource" path="res://fish/species/grouper_red/grouper_red.tres" id="17_grouper_red"] +[ext_resource type="Resource" path="res://fish/species/mackerel_atlantic/mackerel_atlantic.tres" id="18_mackerel_atlantic"] +[ext_resource type="Resource" path="res://fish/species/mackerel_cero/mackerel_cero.tres" id="19_mackerel_cero"] +[ext_resource type="Resource" path="res://fish/species/mackerel_chub/mackerel_chub.tres" id="20_mackerel_chub"] +[ext_resource type="Resource" path="res://fish/species/mackerel_king/mackerel_king.tres" id="21_mackerel_king"] +[ext_resource type="Resource" path="res://fish/species/mackerel_spanish/mackerel_spanish.tres" id="22_mackerel_spanish"] +[ext_resource type="Resource" path="res://fish/species/marlin_black/marlin_black.tres" id="23_marlin_black"] +[ext_resource type="Resource" path="res://fish/species/marlin_blue/marlin_blue.tres" id="24_marlin_blue"] +[ext_resource type="Resource" path="res://fish/species/marlin_white/marlin_white.tres" id="25_marlin_white"] +[ext_resource type="Resource" path="res://fish/species/pomfret_black/pomfret_black.tres" id="26_pomfret_black"] +[ext_resource type="Resource" path="res://fish/species/pomfret_chinese/pomfret_chinese.tres" id="27_pomfret_chinese"] +[ext_resource type="Resource" path="res://fish/species/pomfret_golden/pomfret_golden.tres" id="28_pomfret_golden"] +[ext_resource type="Resource" path="res://fish/species/pomfret_white/pomfret_white.tres" id="29_pomfret_white"] +[ext_resource type="Resource" path="res://fish/species/sailfish/sailfish.tres" id="30_sailfish"] +[ext_resource type="Resource" path="res://fish/species/snapper_lane/snapper_lane.tres" id="31_snapper_lane"] +[ext_resource type="Resource" path="res://fish/species/snapper_mangrove/snapper_mangrove.tres" id="32_snapper_mangrove"] +[ext_resource type="Resource" path="res://fish/species/snapper_mutton/snapper_mutton.tres" id="33_snapper_mutton"] +[ext_resource type="Resource" path="res://fish/species/snapper_red/snapper_red.tres" id="34_snapper_red"] +[ext_resource type="Resource" path="res://fish/species/swordfish/swordfish.tres" id="35_swordfish"] [resource] script = ExtResource("1_pool") -candidates = [ExtResource("2_bass"), ExtResource("3_sunfish")] +candidates = [ExtResource("2_bass"), ExtResource("3_sunfish"), ExtResource("4_tuna_albacore"), ExtResource("5_tuna_bigeye"), ExtResource("6_tuna_bluefin"), ExtResource("7_tuna_skipjack"), ExtResource("8_tuna_yellowfin"), ExtResource("9_salmon_atlantic"), ExtResource("10_salmon_chum"), ExtResource("11_salmon_coho"), ExtResource("12_salmon_pink"), ExtResource("13_salmon_sockeye"), ExtResource("14_anchovy_european"), ExtResource("15_anchovy_northern"), ExtResource("16_grouper_gulf"), ExtResource("17_grouper_red"), ExtResource("18_mackerel_atlantic"), ExtResource("19_mackerel_cero"), ExtResource("20_mackerel_chub"), ExtResource("21_mackerel_king"), ExtResource("22_mackerel_spanish"), ExtResource("23_marlin_black"), ExtResource("24_marlin_blue"), ExtResource("25_marlin_white"), ExtResource("26_pomfret_black"), ExtResource("27_pomfret_chinese"), ExtResource("28_pomfret_golden"), ExtResource("29_pomfret_white"), ExtResource("30_sailfish"), ExtResource("31_snapper_lane"), ExtResource("32_snapper_mangrove"), ExtResource("33_snapper_mutton"), ExtResource("34_snapper_red"), ExtResource("35_swordfish")] diff --git a/fish/pools/starter_pond_pool.tres b/fish/pools/starter_pond_pool.tres index a594d72..de1a327 100644 --- a/fish/pools/starter_pond_pool.tres +++ b/fish/pools/starter_pond_pool.tres @@ -1,13 +1,26 @@ -[gd_resource type="Resource" load_steps=8 format=3] +[gd_resource type="Resource" load_steps=21 format=3] [ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"] [ext_resource type="Resource" path="res://fish/species/bluegill/bluegill.tres" id="2_bluegill"] -[ext_resource type="Resource" path="res://fish/species/carp/carp.tres" id="4_carp"] -[ext_resource type="Resource" path="res://fish/species/catfish_blue/catfish_blue.tres" id="6_catfish_blue"] -[ext_resource type="Resource" path="res://fish/species/catfish_channel/catfish_channel.tres" id="7_catfish_channel"] -[ext_resource type="Resource" path="res://fish/species/catfish_flathead/catfish_flathead.tres" id="8_catfish_flathead"] -[ext_resource type="Resource" path="res://fish/species/catfish_white/catfish_white.tres" id="9_catfish_white"] +[ext_resource type="Resource" path="res://fish/species/carp/carp.tres" id="3_carp"] +[ext_resource type="Resource" path="res://fish/species/catfish_blue/catfish_blue.tres" id="4_catfish_blue"] +[ext_resource type="Resource" path="res://fish/species/catfish_channel/catfish_channel.tres" id="5_catfish_channel"] +[ext_resource type="Resource" path="res://fish/species/catfish_flathead/catfish_flathead.tres" id="6_catfish_flathead"] +[ext_resource type="Resource" path="res://fish/species/catfish_white/catfish_white.tres" id="7_catfish_white"] +[ext_resource type="Resource" path="res://fish/species/goby_round/goby_round.tres" id="8_goby_round"] +[ext_resource type="Resource" path="res://fish/species/chub_european/chub_european.tres" id="9_chub_european"] +[ext_resource type="Resource" path="res://fish/species/chub_flame/chub_flame.tres" id="10_chub_flame"] +[ext_resource type="Resource" path="res://fish/species/chub_lake/chub_lake.tres" id="11_chub_lake"] +[ext_resource type="Resource" path="res://fish/species/goldfish/goldfish.tres" id="12_goldfish"] +[ext_resource type="Resource" path="res://fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres" id="13_goldfish_bubbleeye"] +[ext_resource type="Resource" path="res://fish/species/sauger/sauger.tres" id="14_sauger"] +[ext_resource type="Resource" path="res://fish/species/saugeye/saugeye.tres" id="15_saugeye"] +[ext_resource type="Resource" path="res://fish/species/trout_cutthroat/trout_cutthroat.tres" id="16_trout_cutthroat"] +[ext_resource type="Resource" path="res://fish/species/trout_golden/trout_golden.tres" id="17_trout_golden"] +[ext_resource type="Resource" path="res://fish/species/trout_rainbow/trout_rainbow.tres" id="18_trout_rainbow"] +[ext_resource type="Resource" path="res://fish/species/trout_steelhead/trout_steelhead.tres" id="19_trout_steelhead"] +[ext_resource type="Resource" path="res://fish/species/walleye/walleye.tres" id="20_walleye"] [resource] script = ExtResource("1_pool") -candidates = [ExtResource("2_bluegill"), ExtResource("4_carp"), ExtResource("6_catfish_blue"), ExtResource("7_catfish_channel"), ExtResource("8_catfish_flathead"), ExtResource("9_catfish_white")] +candidates = [ExtResource("2_bluegill"), ExtResource("3_carp"), ExtResource("4_catfish_blue"), ExtResource("5_catfish_channel"), ExtResource("6_catfish_flathead"), ExtResource("7_catfish_white"), ExtResource("8_goby_round"), ExtResource("9_chub_european"), ExtResource("10_chub_flame"), ExtResource("11_chub_lake"), ExtResource("12_goldfish"), ExtResource("13_goldfish_bubbleeye"), ExtResource("14_sauger"), ExtResource("15_saugeye"), ExtResource("16_trout_cutthroat"), ExtResource("17_trout_golden"), ExtResource("18_trout_rainbow"), ExtResource("19_trout_steelhead"), ExtResource("20_walleye")] diff --git a/fish/species/anchovy_european/anchovy_european.tres b/fish/species/anchovy_european/anchovy_european.tres new file mode 100644 index 0000000..fea09ed --- /dev/null +++ b/fish/species/anchovy_european/anchovy_european.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://6a063go0dcqk"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://debgohfsg4ec7" path="res://fish/species/anchovy_european/fish_anchovy_european.png" id="4_texture"] + +[sub_resource type="Resource" id="AnchovyEuropeanAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"anchovy_european" +display_name = "european anchovy" +allowed_water_types = 2 +base_catch_weight = 0.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("AnchovyEuropeanAvailability") +weight_min_lb = 0.05 +weight_max_lb = 0.25 +display_scale_min = 0.85 +display_scale_max = 1.0 +display_scale_curve = 0.9 +sell_value_min = 2 +sell_value_max = 4 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/anchovy_european/fish_anchovy_european.png b/fish/species/anchovy_european/fish_anchovy_european.png new file mode 100644 index 0000000..44742f8 Binary files /dev/null and b/fish/species/anchovy_european/fish_anchovy_european.png differ diff --git a/fish/species/anchovy_european/fish_anchovy_european.png.import b/fish/species/anchovy_european/fish_anchovy_european.png.import new file mode 100644 index 0000000..c4fd63a --- /dev/null +++ b/fish/species/anchovy_european/fish_anchovy_european.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://debgohfsg4ec7" +path="res://.godot/imported/fish_anchovy_european.png-67d070da34e29a3123fed0030af0d694.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/anchovy_european/fish_anchovy_european.png" +dest_files=["res://.godot/imported/fish_anchovy_european.png-67d070da34e29a3123fed0030af0d694.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/anchovy_northern/anchovy_northern.tres b/fish/species/anchovy_northern/anchovy_northern.tres new file mode 100644 index 0000000..ad53d6e --- /dev/null +++ b/fish/species/anchovy_northern/anchovy_northern.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://c30colwbajqf8"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://6sikqc8eivta" path="res://fish/species/anchovy_northern/fish_anchovy_northern.png" id="4_texture"] + +[sub_resource type="Resource" id="AnchovyNorthernAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"anchovy_northern" +display_name = "northern anchovy" +allowed_water_types = 2 +base_catch_weight = 0.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("AnchovyNorthernAvailability") +weight_min_lb = 0.05 +weight_max_lb = 0.35 +display_scale_min = 0.85 +display_scale_max = 1.0 +display_scale_curve = 0.9 +sell_value_min = 2 +sell_value_max = 4 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/anchovy_northern/fish_anchovy_northern.png b/fish/species/anchovy_northern/fish_anchovy_northern.png new file mode 100644 index 0000000..20bacd8 Binary files /dev/null and b/fish/species/anchovy_northern/fish_anchovy_northern.png differ diff --git a/fish/species/anchovy_northern/fish_anchovy_northern.png.import b/fish/species/anchovy_northern/fish_anchovy_northern.png.import new file mode 100644 index 0000000..68ae1ea --- /dev/null +++ b/fish/species/anchovy_northern/fish_anchovy_northern.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6sikqc8eivta" +path="res://.godot/imported/fish_anchovy_northern.png-b1d3cbc1c8208168c212589a431f1227.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/anchovy_northern/fish_anchovy_northern.png" +dest_files=["res://.godot/imported/fish_anchovy_northern.png-b1d3cbc1c8208168c212589a431f1227.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/catfish_blue/catfish_blue.tres b/fish/species/catfish_blue/catfish_blue.tres index 67065dd..482520a 100644 --- a/fish/species/catfish_blue/catfish_blue.tres +++ b/fish/species/catfish_blue/catfish_blue.tres @@ -9,7 +9,6 @@ script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm", &"minnow"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_channel/catfish_channel.tres b/fish/species/catfish_channel/catfish_channel.tres index ae8b0a4..265daad 100644 --- a/fish/species/catfish_channel/catfish_channel.tres +++ b/fish/species/catfish_channel/catfish_channel.tres @@ -9,7 +9,6 @@ script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_flathead/catfish_flathead.tres b/fish/species/catfish_flathead/catfish_flathead.tres index d2cb8a7..651359a 100644 --- a/fish/species/catfish_flathead/catfish_flathead.tres +++ b/fish/species/catfish_flathead/catfish_flathead.tres @@ -9,7 +9,6 @@ script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm", &"minnow"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_white/catfish_white.tres b/fish/species/catfish_white/catfish_white.tres index 2238d1f..6ee72ea 100644 --- a/fish/species/catfish_white/catfish_white.tres +++ b/fish/species/catfish_white/catfish_white.tres @@ -9,7 +9,6 @@ script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/chub_european/chub_european.tres b/fish/species/chub_european/chub_european.tres new file mode 100644 index 0000000..e282b90 --- /dev/null +++ b/fish/species/chub_european/chub_european.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cmyhlwqrx36wg"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://c04wtbyslmpll" path="res://fish/species/chub_european/fish_chub_european.png" id="4_texture"] + +[sub_resource type="Resource" id="ChubEuropeanAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"chub_european" +display_name = "european chub" +allowed_water_types = 1 +base_catch_weight = 0.55 +catch_profile = ExtResource("2_profile") +availability = SubResource("ChubEuropeanAvailability") +weight_min_lb = 0.2 +weight_max_lb = 6.0 +display_scale_min = 0.85 +display_scale_curve = 0.9 +sell_value_min = 2 +sell_value_max = 4 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/chub_european/fish_chub_european.png b/fish/species/chub_european/fish_chub_european.png new file mode 100644 index 0000000..03ed536 Binary files /dev/null and b/fish/species/chub_european/fish_chub_european.png differ diff --git a/fish/species/chub_european/fish_chub_european.png.import b/fish/species/chub_european/fish_chub_european.png.import new file mode 100644 index 0000000..0c241b0 --- /dev/null +++ b/fish/species/chub_european/fish_chub_european.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c04wtbyslmpll" +path="res://.godot/imported/fish_chub_european.png-da2784298a62b1d90038dacb4cf25e35.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/chub_european/fish_chub_european.png" +dest_files=["res://.godot/imported/fish_chub_european.png-da2784298a62b1d90038dacb4cf25e35.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/chub_flame/chub_flame.tres b/fish/species/chub_flame/chub_flame.tres new file mode 100644 index 0000000..d631ab0 --- /dev/null +++ b/fish/species/chub_flame/chub_flame.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://d25q23kfdhm3o"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bla6pc21imxsi" path="res://fish/species/chub_flame/fish_chub_flame.png" id="4_texture"] + +[sub_resource type="Resource" id="ChubFlameAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"chub_flame" +display_name = "flame chub" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.4 +catch_profile = ExtResource("2_profile") +availability = SubResource("ChubFlameAvailability") +weight_min_lb = 0.05 +weight_max_lb = 0.15 +display_scale_min = 0.85 +display_scale_max = 1.1 +display_scale_curve = 0.9 +sell_value_min = 3 +sell_value_max = 6 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/chub_flame/fish_chub_flame.png b/fish/species/chub_flame/fish_chub_flame.png new file mode 100644 index 0000000..98c67e3 Binary files /dev/null and b/fish/species/chub_flame/fish_chub_flame.png differ diff --git a/fish/species/chub_flame/fish_chub_flame.png.import b/fish/species/chub_flame/fish_chub_flame.png.import new file mode 100644 index 0000000..8cad0d9 --- /dev/null +++ b/fish/species/chub_flame/fish_chub_flame.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bla6pc21imxsi" +path="res://.godot/imported/fish_chub_flame.png-9c7780910f16d333d5109ee4e80aa8fb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/chub_flame/fish_chub_flame.png" +dest_files=["res://.godot/imported/fish_chub_flame.png-9c7780910f16d333d5109ee4e80aa8fb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/chub_lake/chub_lake.tres b/fish/species/chub_lake/chub_lake.tres new file mode 100644 index 0000000..c04c5b3 --- /dev/null +++ b/fish/species/chub_lake/chub_lake.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://n7u2krjf4ril"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://w8cjrpnv350h" path="res://fish/species/chub_lake/fish_chub_lake.png" id="4_texture"] + +[sub_resource type="Resource" id="ChubLakeAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"chub_lake" +display_name = "lake chub" +allowed_water_types = 1 +base_catch_weight = 0.55 +catch_profile = ExtResource("2_profile") +availability = SubResource("ChubLakeAvailability") +weight_min_lb = 0.05 +weight_max_lb = 0.5 +display_scale_min = 0.85 +display_scale_max = 1.0 +display_scale_curve = 0.9 +sell_value_min = 2 +sell_value_max = 4 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/chub_lake/fish_chub_lake.png b/fish/species/chub_lake/fish_chub_lake.png new file mode 100644 index 0000000..73101f6 Binary files /dev/null and b/fish/species/chub_lake/fish_chub_lake.png differ diff --git a/fish/species/chub_lake/fish_chub_lake.png.import b/fish/species/chub_lake/fish_chub_lake.png.import new file mode 100644 index 0000000..1be73f0 --- /dev/null +++ b/fish/species/chub_lake/fish_chub_lake.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://w8cjrpnv350h" +path="res://.godot/imported/fish_chub_lake.png-cbb7f27fe4a21820691bbb9c3b9bd3e9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/chub_lake/fish_chub_lake.png" +dest_files=["res://.godot/imported/fish_chub_lake.png-cbb7f27fe4a21820691bbb9c3b9bd3e9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/goby_round/fish_goby_round.png b/fish/species/goby_round/fish_goby_round.png new file mode 100644 index 0000000..89e3f78 Binary files /dev/null and b/fish/species/goby_round/fish_goby_round.png differ diff --git a/fish/species/goby_round/fish_goby_round.png.import b/fish/species/goby_round/fish_goby_round.png.import new file mode 100644 index 0000000..6fb96e8 --- /dev/null +++ b/fish/species/goby_round/fish_goby_round.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdhywe0r1xcio" +path="res://.godot/imported/fish_goby_round.png-3c3fd3dd06c7b5dd4539ab186bf0d865.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/goby_round/fish_goby_round.png" +dest_files=["res://.godot/imported/fish_goby_round.png-3c3fd3dd06c7b5dd4539ab186bf0d865.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/goby_round/goby_round.tres b/fish/species/goby_round/goby_round.tres new file mode 100644 index 0000000..4c8872e --- /dev/null +++ b/fish/species/goby_round/goby_round.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://o1pojc8xthiw"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bdhywe0r1xcio" path="res://fish/species/goby_round/fish_goby_round.png" id="4_texture"] + +[sub_resource type="Resource" id="RoundGobyAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"worm", &"minnow"]) +preferred_bait_weight_multiplier = 1.35 + +[resource] +script = ExtResource("1_fish_data") +id = &"goby_round" +display_name = "round goby" +allowed_water_types = 1 +base_catch_weight = 8.0 +catch_profile = ExtResource("2_profile") +availability = SubResource("RoundGobyAvailability") +weight_min_lb = 0.1 +weight_max_lb = 0.6 +display_scale_min = 0.55 +display_scale_max = 0.9 +sell_value_min = 2 +sell_value_max = 3 +display_texture = ExtResource("4_texture") diff --git a/fish/species/goldfish/fish_goldfish.png b/fish/species/goldfish/fish_goldfish.png new file mode 100644 index 0000000..7ca035b Binary files /dev/null and b/fish/species/goldfish/fish_goldfish.png differ diff --git a/fish/species/goldfish/fish_goldfish.png.import b/fish/species/goldfish/fish_goldfish.png.import new file mode 100644 index 0000000..4d9a960 --- /dev/null +++ b/fish/species/goldfish/fish_goldfish.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c6uth7aob03kg" +path="res://.godot/imported/fish_goldfish.png-567cddb5ae1fc7839cc6dbc80a9a23f3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/goldfish/fish_goldfish.png" +dest_files=["res://.godot/imported/fish_goldfish.png-567cddb5ae1fc7839cc6dbc80a9a23f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/goldfish/goldfish.tres b/fish/species/goldfish/goldfish.tres new file mode 100644 index 0000000..153365d --- /dev/null +++ b/fish/species/goldfish/goldfish.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cw7vdjhhfgtax"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://c6uth7aob03kg" path="res://fish/species/goldfish/fish_goldfish.png" id="4_texture"] + +[sub_resource type="Resource" id="GoldfishAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"goldfish" +display_name = "goldfish" +allowed_water_types = 1 +base_catch_weight = 0.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("GoldfishAvailability") +weight_min_lb = 0.1 +weight_max_lb = 4.0 +display_scale_min = 0.85 +display_scale_max = 1.1 +display_scale_curve = 0.9 +sell_value_min = 1 +sell_value_max = 3 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png new file mode 100644 index 0000000..b621e16 Binary files /dev/null and b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png differ diff --git a/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png.import b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png.import new file mode 100644 index 0000000..1344959 --- /dev/null +++ b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brjjnxr6n7olr" +path="res://.godot/imported/fish_goldfish_bubbleeye.png-a8dcb7e4fb9c7a1beebe2852d2d38d80.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png" +dest_files=["res://.godot/imported/fish_goldfish_bubbleeye.png-a8dcb7e4fb9c7a1beebe2852d2d38d80.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres b/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres new file mode 100644 index 0000000..d6a288a --- /dev/null +++ b/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://beiutt7wq87rp"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://brjjnxr6n7olr" path="res://fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png" id="4_texture"] + +[sub_resource type="Resource" id="GoldfishBubbleeyeAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"goldfish_bubbleeye" +display_name = "bubble-eye goldfish" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("GoldfishBubbleeyeAvailability") +weight_min_lb = 0.1 +weight_max_lb = 0.35 +display_scale_min = 0.85 +display_scale_max = 1.0 +display_scale_curve = 0.9 +sell_value_min = 3 +sell_value_max = 7 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/grouper_gulf/fish_grouper_gulf.png b/fish/species/grouper_gulf/fish_grouper_gulf.png new file mode 100644 index 0000000..e74ea10 Binary files /dev/null and b/fish/species/grouper_gulf/fish_grouper_gulf.png differ diff --git a/fish/species/grouper_gulf/fish_grouper_gulf.png.import b/fish/species/grouper_gulf/fish_grouper_gulf.png.import new file mode 100644 index 0000000..c14f066 --- /dev/null +++ b/fish/species/grouper_gulf/fish_grouper_gulf.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ctu8xd5or7v3h" +path="res://.godot/imported/fish_grouper_gulf.png-8313134ee6830c8e9208b9e3cf5c5333.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/grouper_gulf/fish_grouper_gulf.png" +dest_files=["res://.godot/imported/fish_grouper_gulf.png-8313134ee6830c8e9208b9e3cf5c5333.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/grouper_gulf/grouper_gulf.tres b/fish/species/grouper_gulf/grouper_gulf.tres new file mode 100644 index 0000000..1e0446e --- /dev/null +++ b/fish/species/grouper_gulf/grouper_gulf.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://c5w5e2rkqiaep"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://ctu8xd5or7v3h" path="res://fish/species/grouper_gulf/fish_grouper_gulf.png" id="4_texture"] + +[sub_resource type="Resource" id="GrouperGulfAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"grouper_gulf" +display_name = "gulf grouper" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.3 +catch_profile = ExtResource("2_profile") +availability = SubResource("GrouperGulfAvailability") +weight_min_lb = 2 +weight_max_lb = 40 +display_scale_min = 0.85 +display_scale_max = 1.8 +display_scale_curve = 0.9 +sell_value_min = 10 +sell_value_max = 24 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/grouper_red/fish_grouper_red.png b/fish/species/grouper_red/fish_grouper_red.png new file mode 100644 index 0000000..33b5481 Binary files /dev/null and b/fish/species/grouper_red/fish_grouper_red.png differ diff --git a/fish/species/grouper_red/fish_grouper_red.png.import b/fish/species/grouper_red/fish_grouper_red.png.import new file mode 100644 index 0000000..bd8eac9 --- /dev/null +++ b/fish/species/grouper_red/fish_grouper_red.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://jjcc78tffxhb" +path="res://.godot/imported/fish_grouper_red.png-6056013819bdaaeb55a98fa0f9d8ca75.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/grouper_red/fish_grouper_red.png" +dest_files=["res://.godot/imported/fish_grouper_red.png-6056013819bdaaeb55a98fa0f9d8ca75.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/grouper_red/grouper_red.tres b/fish/species/grouper_red/grouper_red.tres new file mode 100644 index 0000000..5594641 --- /dev/null +++ b/fish/species/grouper_red/grouper_red.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://ckr1c747mvjl0"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://jjcc78tffxhb" path="res://fish/species/grouper_red/fish_grouper_red.png" id="4_texture"] + +[sub_resource type="Resource" id="GrouperRedAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"grouper_red" +display_name = "red grouper" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 0.2 +catch_profile = ExtResource("2_profile") +availability = SubResource("GrouperRedAvailability") +weight_min_lb = 2 +weight_max_lb = 30 +display_scale_min = 0.85 +display_scale_max = 2.0 +display_scale_curve = 0.9 +sell_value_min = 16 +sell_value_max = 36 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png b/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png new file mode 100644 index 0000000..67a2410 Binary files /dev/null and b/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png differ diff --git a/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png.import b/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png.import new file mode 100644 index 0000000..424fcda --- /dev/null +++ b/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://breyc8fqq8lq3" +path="res://.godot/imported/fish_mackerel_atlantic.png-51809cdfc48d2f0c69100567067a64e8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/mackerel_atlantic/fish_mackerel_atlantic.png" +dest_files=["res://.godot/imported/fish_mackerel_atlantic.png-51809cdfc48d2f0c69100567067a64e8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/mackerel_atlantic/mackerel_atlantic.tres b/fish/species/mackerel_atlantic/mackerel_atlantic.tres new file mode 100644 index 0000000..514ef07 --- /dev/null +++ b/fish/species/mackerel_atlantic/mackerel_atlantic.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cm8hgt2qubpi8"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://breyc8fqq8lq3" path="res://fish/species/mackerel_atlantic/fish_mackerel_atlantic.png" id="4_texture"] + +[sub_resource type="Resource" id="MackerelAtlanticAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"mackerel_atlantic" +display_name = "atlantic mackerel" +allowed_water_types = 2 +base_catch_weight = 0.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("MackerelAtlanticAvailability") +weight_max_lb = 5 +display_scale_min = 0.85 +display_scale_max = 1.4 +display_scale_curve = 0.9 +sell_value_min = 4 +sell_value_max = 9 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/mackerel_cero/fish_mackerel_cero.png b/fish/species/mackerel_cero/fish_mackerel_cero.png new file mode 100644 index 0000000..82cb340 Binary files /dev/null and b/fish/species/mackerel_cero/fish_mackerel_cero.png differ diff --git a/fish/species/mackerel_cero/fish_mackerel_cero.png.import b/fish/species/mackerel_cero/fish_mackerel_cero.png.import new file mode 100644 index 0000000..131c8fc --- /dev/null +++ b/fish/species/mackerel_cero/fish_mackerel_cero.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://g8qanq47g1b8" +path="res://.godot/imported/fish_mackerel_cero.png-8da33920501446c8e4d87a07f02f0710.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/mackerel_cero/fish_mackerel_cero.png" +dest_files=["res://.godot/imported/fish_mackerel_cero.png-8da33920501446c8e4d87a07f02f0710.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/mackerel_cero/mackerel_cero.tres b/fish/species/mackerel_cero/mackerel_cero.tres new file mode 100644 index 0000000..fbbb612 --- /dev/null +++ b/fish/species/mackerel_cero/mackerel_cero.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://du4ixk4sssvac"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://g8qanq47g1b8" path="res://fish/species/mackerel_cero/fish_mackerel_cero.png" id="4_texture"] + +[sub_resource type="Resource" id="MackerelCeroAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"mackerel_cero" +display_name = "cero mackerel" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("MackerelCeroAvailability") +weight_min_lb = 1 +weight_max_lb = 15 +display_scale_min = 0.85 +display_scale_max = 1.5 +display_scale_curve = 0.9 +sell_value_min = 7 +sell_value_max = 15 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/mackerel_chub/fish_mackerel_chub.png b/fish/species/mackerel_chub/fish_mackerel_chub.png new file mode 100644 index 0000000..c0687ff Binary files /dev/null and b/fish/species/mackerel_chub/fish_mackerel_chub.png differ diff --git a/fish/species/mackerel_chub/fish_mackerel_chub.png.import b/fish/species/mackerel_chub/fish_mackerel_chub.png.import new file mode 100644 index 0000000..d6be52b --- /dev/null +++ b/fish/species/mackerel_chub/fish_mackerel_chub.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7q2t02oo2bcp" +path="res://.godot/imported/fish_mackerel_chub.png-16a10a6e686dbb920ba5ae98c9dd10a8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/mackerel_chub/fish_mackerel_chub.png" +dest_files=["res://.godot/imported/fish_mackerel_chub.png-16a10a6e686dbb920ba5ae98c9dd10a8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/mackerel_chub/mackerel_chub.tres b/fish/species/mackerel_chub/mackerel_chub.tres new file mode 100644 index 0000000..f4ceca1 --- /dev/null +++ b/fish/species/mackerel_chub/mackerel_chub.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b6lga2c60qkc4"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://b7q2t02oo2bcp" path="res://fish/species/mackerel_chub/fish_mackerel_chub.png" id="4_texture"] + +[sub_resource type="Resource" id="MackerelChubAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"mackerel_chub" +display_name = "chub mackerel" +allowed_water_types = 2 +base_catch_weight = 0.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("MackerelChubAvailability") +weight_max_lb = 4 +display_scale_min = 0.85 +display_scale_max = 1.4 +display_scale_curve = 0.9 +sell_value_min = 4 +sell_value_max = 9 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/mackerel_king/fish_mackerel_king.png b/fish/species/mackerel_king/fish_mackerel_king.png new file mode 100644 index 0000000..01fd603 Binary files /dev/null and b/fish/species/mackerel_king/fish_mackerel_king.png differ diff --git a/fish/species/mackerel_king/fish_mackerel_king.png.import b/fish/species/mackerel_king/fish_mackerel_king.png.import new file mode 100644 index 0000000..213307a --- /dev/null +++ b/fish/species/mackerel_king/fish_mackerel_king.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2fag3xemyx5s" +path="res://.godot/imported/fish_mackerel_king.png-6cd4b6aef031a72436383f7e75f54cc9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/mackerel_king/fish_mackerel_king.png" +dest_files=["res://.godot/imported/fish_mackerel_king.png-6cd4b6aef031a72436383f7e75f54cc9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/mackerel_king/mackerel_king.tres b/fish/species/mackerel_king/mackerel_king.tres new file mode 100644 index 0000000..2c28071 --- /dev/null +++ b/fish/species/mackerel_king/mackerel_king.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://bacndgfbn8oj7"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://b2fag3xemyx5s" path="res://fish/species/mackerel_king/fish_mackerel_king.png" id="4_texture"] + +[sub_resource type="Resource" id="MackerelKingAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"mackerel_king" +display_name = "king mackerel" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.3 +catch_profile = ExtResource("2_profile") +availability = SubResource("MackerelKingAvailability") +weight_min_lb = 3 +weight_max_lb = 40 +display_scale_min = 0.85 +display_scale_max = 1.8 +display_scale_curve = 0.9 +sell_value_min = 10 +sell_value_max = 24 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/mackerel_spanish/fish_mackerel_spanish.png b/fish/species/mackerel_spanish/fish_mackerel_spanish.png new file mode 100644 index 0000000..2a97bed Binary files /dev/null and b/fish/species/mackerel_spanish/fish_mackerel_spanish.png differ diff --git a/fish/species/mackerel_spanish/fish_mackerel_spanish.png.import b/fish/species/mackerel_spanish/fish_mackerel_spanish.png.import new file mode 100644 index 0000000..00d8314 --- /dev/null +++ b/fish/species/mackerel_spanish/fish_mackerel_spanish.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cy2hmpenpkee2" +path="res://.godot/imported/fish_mackerel_spanish.png-14336b47fbd7fc1f1bcf0ce301b2b130.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/mackerel_spanish/fish_mackerel_spanish.png" +dest_files=["res://.godot/imported/fish_mackerel_spanish.png-14336b47fbd7fc1f1bcf0ce301b2b130.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/mackerel_spanish/mackerel_spanish.tres b/fish/species/mackerel_spanish/mackerel_spanish.tres new file mode 100644 index 0000000..0319917 --- /dev/null +++ b/fish/species/mackerel_spanish/mackerel_spanish.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cg7y0numxuy3e"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cy2hmpenpkee2" path="res://fish/species/mackerel_spanish/fish_mackerel_spanish.png" id="4_texture"] + +[sub_resource type="Resource" id="MackerelSpanishAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"mackerel_spanish" +display_name = "spanish mackerel" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("MackerelSpanishAvailability") +weight_min_lb = 1 +weight_max_lb = 15 +display_scale_min = 0.85 +display_scale_max = 1.6 +display_scale_curve = 0.9 +sell_value_min = 8 +sell_value_max = 18 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/marlin_black/fish_marlin_black.png b/fish/species/marlin_black/fish_marlin_black.png new file mode 100644 index 0000000..6cbcccd Binary files /dev/null and b/fish/species/marlin_black/fish_marlin_black.png differ diff --git a/fish/species/marlin_black/fish_marlin_black.png.import b/fish/species/marlin_black/fish_marlin_black.png.import new file mode 100644 index 0000000..429803e --- /dev/null +++ b/fish/species/marlin_black/fish_marlin_black.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkdctr2o52vrb" +path="res://.godot/imported/fish_marlin_black.png-8e8b83d60a3f56d5d14f9e7c3ce2a9f9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/marlin_black/fish_marlin_black.png" +dest_files=["res://.godot/imported/fish_marlin_black.png-8e8b83d60a3f56d5d14f9e7c3ce2a9f9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/marlin_black/marlin_black.tres b/fish/species/marlin_black/marlin_black.tres new file mode 100644 index 0000000..39110c5 --- /dev/null +++ b/fish/species/marlin_black/marlin_black.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://dlm608g365aa3"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://dkdctr2o52vrb" path="res://fish/species/marlin_black/fish_marlin_black.png" id="4_texture"] + +[sub_resource type="Resource" id="MarlinBlackAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"marlin_black" +display_name = "black marlin" +allowed_water_types = 2 +rarity = 3 +base_catch_weight = 0.08 +catch_profile = ExtResource("2_profile") +availability = SubResource("MarlinBlackAvailability") +weight_min_lb = 80 +weight_max_lb = 700 +display_scale_min = 0.85 +display_scale_max = 2.8 +display_scale_curve = 0.9 +sell_value_min = 45 +sell_value_max = 90 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/marlin_blue/fish_marlin_blue.png b/fish/species/marlin_blue/fish_marlin_blue.png new file mode 100644 index 0000000..7df059f Binary files /dev/null and b/fish/species/marlin_blue/fish_marlin_blue.png differ diff --git a/fish/species/marlin_blue/fish_marlin_blue.png.import b/fish/species/marlin_blue/fish_marlin_blue.png.import new file mode 100644 index 0000000..73053d8 --- /dev/null +++ b/fish/species/marlin_blue/fish_marlin_blue.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://de35fjoytbomu" +path="res://.godot/imported/fish_marlin_blue.png-268aff4aa2760732f42f94b881681364.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/marlin_blue/fish_marlin_blue.png" +dest_files=["res://.godot/imported/fish_marlin_blue.png-268aff4aa2760732f42f94b881681364.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/marlin_blue/marlin_blue.tres b/fish/species/marlin_blue/marlin_blue.tres new file mode 100644 index 0000000..b3328ba --- /dev/null +++ b/fish/species/marlin_blue/marlin_blue.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b15syao6ighl4"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://de35fjoytbomu" path="res://fish/species/marlin_blue/fish_marlin_blue.png" id="4_texture"] + +[sub_resource type="Resource" id="MarlinBlueAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"marlin_blue" +display_name = "blue marlin" +allowed_water_types = 2 +rarity = 4 +base_catch_weight = 0.05 +catch_profile = ExtResource("2_profile") +availability = SubResource("MarlinBlueAvailability") +weight_min_lb = 100 +weight_max_lb = 1000 +display_scale_min = 0.85 +display_scale_max = 3.0 +display_scale_curve = 0.9 +sell_value_min = 60 +sell_value_max = 120 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/marlin_white/fish_marlin_white.png b/fish/species/marlin_white/fish_marlin_white.png new file mode 100644 index 0000000..76fb780 Binary files /dev/null and b/fish/species/marlin_white/fish_marlin_white.png differ diff --git a/fish/species/marlin_white/fish_marlin_white.png.import b/fish/species/marlin_white/fish_marlin_white.png.import new file mode 100644 index 0000000..1ec83d0 --- /dev/null +++ b/fish/species/marlin_white/fish_marlin_white.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdvy4ujx2htcy" +path="res://.godot/imported/fish_marlin_white.png-a5c5fec8891d945d6565d5097e312c31.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/marlin_white/fish_marlin_white.png" +dest_files=["res://.godot/imported/fish_marlin_white.png-a5c5fec8891d945d6565d5097e312c31.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/marlin_white/marlin_white.tres b/fish/species/marlin_white/marlin_white.tres new file mode 100644 index 0000000..e90651c --- /dev/null +++ b/fish/species/marlin_white/marlin_white.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://jjxr31djus2h"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bdvy4ujx2htcy" path="res://fish/species/marlin_white/fish_marlin_white.png" id="4_texture"] + +[sub_resource type="Resource" id="MarlinWhiteAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"marlin_white" +display_name = "white marlin" +allowed_water_types = 2 +rarity = 3 +base_catch_weight = 0.1 +catch_profile = ExtResource("2_profile") +availability = SubResource("MarlinWhiteAvailability") +weight_min_lb = 30 +weight_max_lb = 180 +display_scale_min = 0.85 +display_scale_max = 2.5 +display_scale_curve = 0.9 +sell_value_min = 35 +sell_value_max = 70 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/pomfret_black/fish_pomfret_black.png b/fish/species/pomfret_black/fish_pomfret_black.png new file mode 100644 index 0000000..14ae878 Binary files /dev/null and b/fish/species/pomfret_black/fish_pomfret_black.png differ diff --git a/fish/species/pomfret_black/fish_pomfret_black.png.import b/fish/species/pomfret_black/fish_pomfret_black.png.import new file mode 100644 index 0000000..c8748a8 --- /dev/null +++ b/fish/species/pomfret_black/fish_pomfret_black.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ch3y6xdo22j1o" +path="res://.godot/imported/fish_pomfret_black.png-f757c46fb76cb4f9eee68c3143813d3f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/pomfret_black/fish_pomfret_black.png" +dest_files=["res://.godot/imported/fish_pomfret_black.png-f757c46fb76cb4f9eee68c3143813d3f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/pomfret_black/pomfret_black.tres b/fish/species/pomfret_black/pomfret_black.tres new file mode 100644 index 0000000..8020100 --- /dev/null +++ b/fish/species/pomfret_black/pomfret_black.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://bsm1itwx50p1q"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://ch3y6xdo22j1o" path="res://fish/species/pomfret_black/fish_pomfret_black.png" id="4_texture"] + +[sub_resource type="Resource" id="PomfretBlackAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"pomfret_black" +display_name = "black pomfret" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.3 +catch_profile = ExtResource("2_profile") +availability = SubResource("PomfretBlackAvailability") +weight_min_lb = 1 +weight_max_lb = 15 +display_scale_min = 0.85 +display_scale_max = 1.8 +display_scale_curve = 0.9 +sell_value_min = 8 +sell_value_max = 18 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/pomfret_chinese/fish_pomfret_chinese.png b/fish/species/pomfret_chinese/fish_pomfret_chinese.png new file mode 100644 index 0000000..a7bb55e Binary files /dev/null and b/fish/species/pomfret_chinese/fish_pomfret_chinese.png differ diff --git a/fish/species/pomfret_chinese/fish_pomfret_chinese.png.import b/fish/species/pomfret_chinese/fish_pomfret_chinese.png.import new file mode 100644 index 0000000..cdf2c5b --- /dev/null +++ b/fish/species/pomfret_chinese/fish_pomfret_chinese.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cijr0gseldncc" +path="res://.godot/imported/fish_pomfret_chinese.png-a68ccb2e407266ab98980bb3087760ad.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/pomfret_chinese/fish_pomfret_chinese.png" +dest_files=["res://.godot/imported/fish_pomfret_chinese.png-a68ccb2e407266ab98980bb3087760ad.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/pomfret_chinese/pomfret_chinese.tres b/fish/species/pomfret_chinese/pomfret_chinese.tres new file mode 100644 index 0000000..2eaf34b --- /dev/null +++ b/fish/species/pomfret_chinese/pomfret_chinese.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://di8qq1hek8cpo"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cijr0gseldncc" path="res://fish/species/pomfret_chinese/fish_pomfret_chinese.png" id="4_texture"] + +[sub_resource type="Resource" id="PomfretChineseAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"pomfret_chinese" +display_name = "chinese pomfret" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.3 +catch_profile = ExtResource("2_profile") +availability = SubResource("PomfretChineseAvailability") +weight_min_lb = 1 +weight_max_lb = 20 +display_scale_min = 0.85 +display_scale_max = 1.9 +display_scale_curve = 0.9 +sell_value_min = 9 +sell_value_max = 20 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/pomfret_golden/fish_pomfret_golden.png b/fish/species/pomfret_golden/fish_pomfret_golden.png new file mode 100644 index 0000000..a844b87 Binary files /dev/null and b/fish/species/pomfret_golden/fish_pomfret_golden.png differ diff --git a/fish/species/pomfret_golden/fish_pomfret_golden.png.import b/fish/species/pomfret_golden/fish_pomfret_golden.png.import new file mode 100644 index 0000000..bd77922 --- /dev/null +++ b/fish/species/pomfret_golden/fish_pomfret_golden.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7rjv7k60bv7q" +path="res://.godot/imported/fish_pomfret_golden.png-932aebb2b7e0fef891368bcd8e2ca1c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/pomfret_golden/fish_pomfret_golden.png" +dest_files=["res://.godot/imported/fish_pomfret_golden.png-932aebb2b7e0fef891368bcd8e2ca1c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/pomfret_golden/pomfret_golden.tres b/fish/species/pomfret_golden/pomfret_golden.tres new file mode 100644 index 0000000..da1f561 --- /dev/null +++ b/fish/species/pomfret_golden/pomfret_golden.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b5ivfnfidlu81"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://7rjv7k60bv7q" path="res://fish/species/pomfret_golden/fish_pomfret_golden.png" id="4_texture"] + +[sub_resource type="Resource" id="PomfretGoldenAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"pomfret_golden" +display_name = "golden pomfret" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 0.18 +catch_profile = ExtResource("2_profile") +availability = SubResource("PomfretGoldenAvailability") +weight_min_lb = 2 +weight_max_lb = 25 +display_scale_min = 0.85 +display_scale_max = 2.1 +display_scale_curve = 0.9 +sell_value_min = 14 +sell_value_max = 30 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/pomfret_white/fish_pomfret_white.png b/fish/species/pomfret_white/fish_pomfret_white.png new file mode 100644 index 0000000..0f2d426 Binary files /dev/null and b/fish/species/pomfret_white/fish_pomfret_white.png differ diff --git a/fish/species/pomfret_white/fish_pomfret_white.png.import b/fish/species/pomfret_white/fish_pomfret_white.png.import new file mode 100644 index 0000000..b3da3f2 --- /dev/null +++ b/fish/species/pomfret_white/fish_pomfret_white.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3fr0x4e1nfe0" +path="res://.godot/imported/fish_pomfret_white.png-10c529e20d581d581fc12328c7d152fe.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/pomfret_white/fish_pomfret_white.png" +dest_files=["res://.godot/imported/fish_pomfret_white.png-10c529e20d581d581fc12328c7d152fe.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/pomfret_white/pomfret_white.tres b/fish/species/pomfret_white/pomfret_white.tres new file mode 100644 index 0000000..fa317c7 --- /dev/null +++ b/fish/species/pomfret_white/pomfret_white.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://6rfka15wc2iy"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://c3fr0x4e1nfe0" path="res://fish/species/pomfret_white/fish_pomfret_white.png" id="4_texture"] + +[sub_resource type="Resource" id="PomfretWhiteAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"pomfret_white" +display_name = "white pomfret" +allowed_water_types = 2 +base_catch_weight = 0.45 +catch_profile = ExtResource("2_profile") +availability = SubResource("PomfretWhiteAvailability") +weight_max_lb = 10 +display_scale_min = 0.85 +display_scale_max = 1.6 +display_scale_curve = 0.9 +sell_value_min = 5 +sell_value_max = 12 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/sailfish/fish_sailfish.png b/fish/species/sailfish/fish_sailfish.png new file mode 100644 index 0000000..e359a3e Binary files /dev/null and b/fish/species/sailfish/fish_sailfish.png differ diff --git a/fish/species/sailfish/fish_sailfish.png.import b/fish/species/sailfish/fish_sailfish.png.import new file mode 100644 index 0000000..ee25011 --- /dev/null +++ b/fish/species/sailfish/fish_sailfish.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8rlcnfqx8or0" +path="res://.godot/imported/fish_sailfish.png-cc20ed43042eb2d6867954aa1a1772da.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/sailfish/fish_sailfish.png" +dest_files=["res://.godot/imported/fish_sailfish.png-cc20ed43042eb2d6867954aa1a1772da.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/sailfish/sailfish.tres b/fish/species/sailfish/sailfish.tres new file mode 100644 index 0000000..7f6bea9 --- /dev/null +++ b/fish/species/sailfish/sailfish.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b3auce81mglkx"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://8rlcnfqx8or0" path="res://fish/species/sailfish/fish_sailfish.png" id="4_texture"] + +[sub_resource type="Resource" id="SailfishAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"sailfish" +display_name = "sailfish" +allowed_water_types = 2 +rarity = 3 +base_catch_weight = 0.1 +catch_profile = ExtResource("2_profile") +availability = SubResource("SailfishAvailability") +weight_min_lb = 30 +weight_max_lb = 200 +display_scale_min = 0.85 +display_scale_max = 2.6 +display_scale_curve = 0.9 +sell_value_min = 38 +sell_value_max = 78 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/salmon_atlantic/fish_salmon_atlantic.png b/fish/species/salmon_atlantic/fish_salmon_atlantic.png new file mode 100644 index 0000000..ea6164b Binary files /dev/null and b/fish/species/salmon_atlantic/fish_salmon_atlantic.png differ diff --git a/fish/species/salmon_atlantic/fish_salmon_atlantic.png.import b/fish/species/salmon_atlantic/fish_salmon_atlantic.png.import new file mode 100644 index 0000000..36fb242 --- /dev/null +++ b/fish/species/salmon_atlantic/fish_salmon_atlantic.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddbclfu5wtd5r" +path="res://.godot/imported/fish_salmon_atlantic.png-e11e70a37a78573bfb86a21b48ae319b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/salmon_atlantic/fish_salmon_atlantic.png" +dest_files=["res://.godot/imported/fish_salmon_atlantic.png-e11e70a37a78573bfb86a21b48ae319b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/salmon_atlantic/salmon_atlantic.tres b/fish/species/salmon_atlantic/salmon_atlantic.tres new file mode 100644 index 0000000..fa3f0d6 --- /dev/null +++ b/fish/species/salmon_atlantic/salmon_atlantic.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b5ewuclck75yr"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://ddbclfu5wtd5r" path="res://fish/species/salmon_atlantic/fish_salmon_atlantic.png" id="4_texture"] + +[sub_resource type="Resource" id="AtlanticSalmonAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"salmon_atlantic" +display_name = "atlantic salmon" +allowed_water_types = 2 +rarity = 3 +base_catch_weight = 0.65 +catch_profile = ExtResource("2_profile") +availability = SubResource("AtlanticSalmonAvailability") +weight_min_lb = 8.0 +weight_max_lb = 40.0 +display_scale_min = 1.05 +display_scale_max = 1.9 +display_scale_curve = 0.85 +sell_value_min = 20 +sell_value_max = 42 +sell_value_curve = 0.85 +display_texture = ExtResource("4_texture") diff --git a/fish/species/salmon_chum/fish_salmon_chum.png b/fish/species/salmon_chum/fish_salmon_chum.png new file mode 100644 index 0000000..920b39d Binary files /dev/null and b/fish/species/salmon_chum/fish_salmon_chum.png differ diff --git a/fish/species/salmon_chum/fish_salmon_chum.png.import b/fish/species/salmon_chum/fish_salmon_chum.png.import new file mode 100644 index 0000000..587b83b --- /dev/null +++ b/fish/species/salmon_chum/fish_salmon_chum.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqjkmd6l1a2i4" +path="res://.godot/imported/fish_salmon_chum.png-78c86aca1218255db2c488a5c22e4409.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/salmon_chum/fish_salmon_chum.png" +dest_files=["res://.godot/imported/fish_salmon_chum.png-78c86aca1218255db2c488a5c22e4409.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/salmon_chum/salmon_chum.tres b/fish/species/salmon_chum/salmon_chum.tres new file mode 100644 index 0000000..748295b --- /dev/null +++ b/fish/species/salmon_chum/salmon_chum.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://dhddj6grq203c"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://dqjkmd6l1a2i4" path="res://fish/species/salmon_chum/fish_salmon_chum.png" id="4_texture"] + +[sub_resource type="Resource" id="ChumSalmonAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"salmon_chum" +display_name = "chum salmon" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 2.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("ChumSalmonAvailability") +weight_min_lb = 6.0 +weight_max_lb = 25.0 +display_scale_min = 1.0 +display_scale_max = 1.75 +display_scale_curve = 0.9 +sell_value_min = 10 +sell_value_max = 22 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/salmon_coho/fish_salmon_coho.png b/fish/species/salmon_coho/fish_salmon_coho.png new file mode 100644 index 0000000..2caf696 Binary files /dev/null and b/fish/species/salmon_coho/fish_salmon_coho.png differ diff --git a/fish/species/salmon_coho/fish_salmon_coho.png.import b/fish/species/salmon_coho/fish_salmon_coho.png.import new file mode 100644 index 0000000..1cd3254 --- /dev/null +++ b/fish/species/salmon_coho/fish_salmon_coho.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4c4e43lbr88c" +path="res://.godot/imported/fish_salmon_coho.png-fc57b7ff3c0ac63ccfec6db9cec1dedb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/salmon_coho/fish_salmon_coho.png" +dest_files=["res://.godot/imported/fish_salmon_coho.png-fc57b7ff3c0ac63ccfec6db9cec1dedb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/salmon_coho/salmon_coho.tres b/fish/species/salmon_coho/salmon_coho.tres new file mode 100644 index 0000000..1c1a783 --- /dev/null +++ b/fish/species/salmon_coho/salmon_coho.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://nw1k2c7vtqtt"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://c4c4e43lbr88c" path="res://fish/species/salmon_coho/fish_salmon_coho.png" id="4_texture"] + +[sub_resource type="Resource" id="CohoSalmonAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"salmon_coho" +display_name = "coho salmon" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 1.4 +catch_profile = ExtResource("2_profile") +availability = SubResource("CohoSalmonAvailability") +weight_min_lb = 5.0 +weight_max_lb = 30.0 +display_scale_min = 1.0 +display_scale_max = 1.8 +display_scale_curve = 0.88 +sell_value_min = 14 +sell_value_max = 28 +sell_value_curve = 0.88 +display_texture = ExtResource("4_texture") diff --git a/fish/species/salmon_pink/fish_salmon_pink.png b/fish/species/salmon_pink/fish_salmon_pink.png new file mode 100644 index 0000000..85b1d88 Binary files /dev/null and b/fish/species/salmon_pink/fish_salmon_pink.png differ diff --git a/fish/species/salmon_pink/fish_salmon_pink.png.import b/fish/species/salmon_pink/fish_salmon_pink.png.import new file mode 100644 index 0000000..caf4c74 --- /dev/null +++ b/fish/species/salmon_pink/fish_salmon_pink.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bk27bsdrtlqap" +path="res://.godot/imported/fish_salmon_pink.png-0c9ca3bc540b868c12fe32b1cdc59249.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/salmon_pink/fish_salmon_pink.png" +dest_files=["res://.godot/imported/fish_salmon_pink.png-0c9ca3bc540b868c12fe32b1cdc59249.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/salmon_pink/salmon_pink.tres b/fish/species/salmon_pink/salmon_pink.tres new file mode 100644 index 0000000..d390cf1 --- /dev/null +++ b/fish/species/salmon_pink/salmon_pink.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cq0ikmuba6j73"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bk27bsdrtlqap" path="res://fish/species/salmon_pink/fish_salmon_pink.png" id="4_texture"] + +[sub_resource type="Resource" id="PinkSalmonAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"salmon_pink" +display_name = "pink salmon" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 3.0 +catch_profile = ExtResource("2_profile") +availability = SubResource("PinkSalmonAvailability") +weight_min_lb = 3.0 +weight_max_lb = 12.0 +display_scale_min = 0.9 +display_scale_max = 1.55 +display_scale_curve = 0.92 +sell_value_min = 8 +sell_value_max = 15 +sell_value_curve = 0.92 +display_texture = ExtResource("4_texture") diff --git a/fish/species/salmon_sockeye/fish_salmon_sockeye.png b/fish/species/salmon_sockeye/fish_salmon_sockeye.png new file mode 100644 index 0000000..bb98433 Binary files /dev/null and b/fish/species/salmon_sockeye/fish_salmon_sockeye.png differ diff --git a/fish/species/salmon_sockeye/fish_salmon_sockeye.png.import b/fish/species/salmon_sockeye/fish_salmon_sockeye.png.import new file mode 100644 index 0000000..71a8856 --- /dev/null +++ b/fish/species/salmon_sockeye/fish_salmon_sockeye.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cmokm3sra4ai8" +path="res://.godot/imported/fish_salmon_sockeye.png-99d566111b5cad1e6ae4702b6f9128fd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/salmon_sockeye/fish_salmon_sockeye.png" +dest_files=["res://.godot/imported/fish_salmon_sockeye.png-99d566111b5cad1e6ae4702b6f9128fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/salmon_sockeye/salmon_sockeye.tres b/fish/species/salmon_sockeye/salmon_sockeye.tres new file mode 100644 index 0000000..deeda39 --- /dev/null +++ b/fish/species/salmon_sockeye/salmon_sockeye.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://bf2ej6rgypsqx"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cmokm3sra4ai8" path="res://fish/species/salmon_sockeye/fish_salmon_sockeye.png" id="4_texture"] + +[sub_resource type="Resource" id="SockeyeSalmonAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"salmon_sockeye" +display_name = "sockeye salmon" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 1.6 +catch_profile = ExtResource("2_profile") +availability = SubResource("SockeyeSalmonAvailability") +weight_min_lb = 4.0 +weight_max_lb = 15.0 +display_scale_min = 0.95 +display_scale_max = 1.6 +display_scale_curve = 0.9 +sell_value_min = 12 +sell_value_max = 24 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/sauger/fish_sauger.png b/fish/species/sauger/fish_sauger.png new file mode 100644 index 0000000..c4e2763 Binary files /dev/null and b/fish/species/sauger/fish_sauger.png differ diff --git a/fish/species/sauger/fish_sauger.png.import b/fish/species/sauger/fish_sauger.png.import new file mode 100644 index 0000000..02110fe --- /dev/null +++ b/fish/species/sauger/fish_sauger.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8k7qkk4juciy" +path="res://.godot/imported/fish_sauger.png-9b3abc6f8648ebbd51a56abeedbb4826.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/sauger/fish_sauger.png" +dest_files=["res://.godot/imported/fish_sauger.png-9b3abc6f8648ebbd51a56abeedbb4826.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/sauger/sauger.tres b/fish/species/sauger/sauger.tres new file mode 100644 index 0000000..49cf5ad --- /dev/null +++ b/fish/species/sauger/sauger.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://suhm4yemc0vf"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://8k7qkk4juciy" path="res://fish/species/sauger/fish_sauger.png" id="4_texture"] + +[sub_resource type="Resource" id="SaugerAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"sauger" +display_name = "sauger" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("SaugerAvailability") +weight_max_lb = 8 +display_scale_min = 0.85 +display_scale_max = 1.4 +display_scale_curve = 0.9 +sell_value_min = 6 +sell_value_max = 12 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/saugeye/fish_saugeye.png b/fish/species/saugeye/fish_saugeye.png new file mode 100644 index 0000000..0674902 Binary files /dev/null and b/fish/species/saugeye/fish_saugeye.png differ diff --git a/fish/species/saugeye/fish_saugeye.png.import b/fish/species/saugeye/fish_saugeye.png.import new file mode 100644 index 0000000..6011e7b --- /dev/null +++ b/fish/species/saugeye/fish_saugeye.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lom25y0eqcw3" +path="res://.godot/imported/fish_saugeye.png-638130dc7e3f534cf48d3da80fc2c3df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/saugeye/fish_saugeye.png" +dest_files=["res://.godot/imported/fish_saugeye.png-638130dc7e3f534cf48d3da80fc2c3df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/saugeye/saugeye.tres b/fish/species/saugeye/saugeye.tres new file mode 100644 index 0000000..e834005 --- /dev/null +++ b/fish/species/saugeye/saugeye.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://oecfrxf7vgh0"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://lom25y0eqcw3" path="res://fish/species/saugeye/fish_saugeye.png" id="4_texture"] + +[sub_resource type="Resource" id="SaugeyeAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"saugeye" +display_name = "saugeye" +allowed_water_types = 1 +rarity = 2 +base_catch_weight = 0.2 +catch_profile = ExtResource("2_profile") +availability = SubResource("SaugeyeAvailability") +weight_max_lb = 12 +display_scale_min = 0.85 +display_scale_max = 1.6 +display_scale_curve = 0.9 +sell_value_min = 9 +sell_value_max = 18 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/snapper_lane/fish_snapper_lane.png b/fish/species/snapper_lane/fish_snapper_lane.png new file mode 100644 index 0000000..afedbc3 Binary files /dev/null and b/fish/species/snapper_lane/fish_snapper_lane.png differ diff --git a/fish/species/snapper_lane/fish_snapper_lane.png.import b/fish/species/snapper_lane/fish_snapper_lane.png.import new file mode 100644 index 0000000..323b6a0 --- /dev/null +++ b/fish/species/snapper_lane/fish_snapper_lane.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3d8s30bxq4tp" +path="res://.godot/imported/fish_snapper_lane.png-e491c67d8214edb53b8a2005c274c743.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/snapper_lane/fish_snapper_lane.png" +dest_files=["res://.godot/imported/fish_snapper_lane.png-e491c67d8214edb53b8a2005c274c743.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/snapper_lane/snapper_lane.tres b/fish/species/snapper_lane/snapper_lane.tres new file mode 100644 index 0000000..e9e7422 --- /dev/null +++ b/fish/species/snapper_lane/snapper_lane.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://b7t3fuykfa6ri"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://d3d8s30bxq4tp" path="res://fish/species/snapper_lane/fish_snapper_lane.png" id="4_texture"] + +[sub_resource type="Resource" id="SnapperLaneAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"snapper_lane" +display_name = "lane snapper" +allowed_water_types = 2 +base_catch_weight = 0.45 +catch_profile = ExtResource("2_profile") +availability = SubResource("SnapperLaneAvailability") +weight_max_lb = 5 +display_scale_min = 0.85 +display_scale_max = 1.5 +display_scale_curve = 0.9 +sell_value_min = 5 +sell_value_max = 11 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/snapper_mangrove/fish_snapper_mangrove.png b/fish/species/snapper_mangrove/fish_snapper_mangrove.png new file mode 100644 index 0000000..758ee16 Binary files /dev/null and b/fish/species/snapper_mangrove/fish_snapper_mangrove.png differ diff --git a/fish/species/snapper_mangrove/fish_snapper_mangrove.png.import b/fish/species/snapper_mangrove/fish_snapper_mangrove.png.import new file mode 100644 index 0000000..7567a29 --- /dev/null +++ b/fish/species/snapper_mangrove/fish_snapper_mangrove.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://v34sdv1v7c2x" +path="res://.godot/imported/fish_snapper_mangrove.png-fb4a657d3596495207f499095e50e416.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/snapper_mangrove/fish_snapper_mangrove.png" +dest_files=["res://.godot/imported/fish_snapper_mangrove.png-fb4a657d3596495207f499095e50e416.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/snapper_mangrove/snapper_mangrove.tres b/fish/species/snapper_mangrove/snapper_mangrove.tres new file mode 100644 index 0000000..58226ad --- /dev/null +++ b/fish/species/snapper_mangrove/snapper_mangrove.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://d4eh51kwhsac0"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://v34sdv1v7c2x" path="res://fish/species/snapper_mangrove/fish_snapper_mangrove.png" id="4_texture"] + +[sub_resource type="Resource" id="SnapperMangroveAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"snapper_mangrove" +display_name = "mangrove snapper" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("SnapperMangroveAvailability") +weight_max_lb = 12 +display_scale_min = 0.85 +display_scale_max = 1.7 +display_scale_curve = 0.9 +sell_value_min = 7 +sell_value_max = 16 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/snapper_mutton/fish_snapper_mutton.png b/fish/species/snapper_mutton/fish_snapper_mutton.png new file mode 100644 index 0000000..f8de3ff Binary files /dev/null and b/fish/species/snapper_mutton/fish_snapper_mutton.png differ diff --git a/fish/species/snapper_mutton/fish_snapper_mutton.png.import b/fish/species/snapper_mutton/fish_snapper_mutton.png.import new file mode 100644 index 0000000..cc283b6 --- /dev/null +++ b/fish/species/snapper_mutton/fish_snapper_mutton.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cawvpo8aysw5i" +path="res://.godot/imported/fish_snapper_mutton.png-c83ebfec373a81cf6c3f34599406aad6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/snapper_mutton/fish_snapper_mutton.png" +dest_files=["res://.godot/imported/fish_snapper_mutton.png-c83ebfec373a81cf6c3f34599406aad6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/snapper_mutton/snapper_mutton.tres b/fish/species/snapper_mutton/snapper_mutton.tres new file mode 100644 index 0000000..6d4441a --- /dev/null +++ b/fish/species/snapper_mutton/snapper_mutton.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cfvwloicmu53b"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cawvpo8aysw5i" path="res://fish/species/snapper_mutton/fish_snapper_mutton.png" id="4_texture"] + +[sub_resource type="Resource" id="SnapperMuttonAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"snapper_mutton" +display_name = "mutton snapper" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 0.2 +catch_profile = ExtResource("2_profile") +availability = SubResource("SnapperMuttonAvailability") +weight_min_lb = 2 +weight_max_lb = 30 +display_scale_min = 0.85 +display_scale_max = 1.9 +display_scale_curve = 0.9 +sell_value_min = 12 +sell_value_max = 28 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/snapper_red/fish_snapper_red.png b/fish/species/snapper_red/fish_snapper_red.png new file mode 100644 index 0000000..435a95e Binary files /dev/null and b/fish/species/snapper_red/fish_snapper_red.png differ diff --git a/fish/species/snapper_red/fish_snapper_red.png.import b/fish/species/snapper_red/fish_snapper_red.png.import new file mode 100644 index 0000000..6412dfc --- /dev/null +++ b/fish/species/snapper_red/fish_snapper_red.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ei5scljuqklk" +path="res://.godot/imported/fish_snapper_red.png-b66be0003a9d62c443a185008180dd96.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/snapper_red/fish_snapper_red.png" +dest_files=["res://.godot/imported/fish_snapper_red.png-b66be0003a9d62c443a185008180dd96.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/snapper_red/snapper_red.tres b/fish/species/snapper_red/snapper_red.tres new file mode 100644 index 0000000..8778c98 --- /dev/null +++ b/fish/species/snapper_red/snapper_red.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cpvi3raa6epib"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://ei5scljuqklk" path="res://fish/species/snapper_red/fish_snapper_red.png" id="4_texture"] + +[sub_resource type="Resource" id="SnapperRedAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"snapper_red" +display_name = "red snapper" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 0.35 +catch_profile = ExtResource("2_profile") +availability = SubResource("SnapperRedAvailability") +weight_min_lb = 1 +weight_max_lb = 25 +display_scale_min = 0.85 +display_scale_max = 1.7 +display_scale_curve = 0.9 +sell_value_min = 8 +sell_value_max = 19 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/swordfish/fish_swordfish.png b/fish/species/swordfish/fish_swordfish.png new file mode 100644 index 0000000..a43d703 Binary files /dev/null and b/fish/species/swordfish/fish_swordfish.png differ diff --git a/fish/species/swordfish/fish_swordfish.png.import b/fish/species/swordfish/fish_swordfish.png.import new file mode 100644 index 0000000..4979773 --- /dev/null +++ b/fish/species/swordfish/fish_swordfish.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cs1qmm4galac4" +path="res://.godot/imported/fish_swordfish.png-c82f3cdc508ed13c9ec1a5b28b6eb830.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/swordfish/fish_swordfish.png" +dest_files=["res://.godot/imported/fish_swordfish.png-c82f3cdc508ed13c9ec1a5b28b6eb830.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/swordfish/swordfish.tres b/fish/species/swordfish/swordfish.tres new file mode 100644 index 0000000..2184395 --- /dev/null +++ b/fish/species/swordfish/swordfish.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://chqva2v2mtm8q"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cs1qmm4galac4" path="res://fish/species/swordfish/fish_swordfish.png" id="4_texture"] + +[sub_resource type="Resource" id="SwordfishAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"ocean"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"swordfish" +display_name = "swordfish" +allowed_water_types = 2 +rarity = 4 +base_catch_weight = 0.06 +catch_profile = ExtResource("2_profile") +availability = SubResource("SwordfishAvailability") +weight_min_lb = 50 +weight_max_lb = 1000 +display_scale_min = 0.85 +display_scale_max = 3.0 +display_scale_curve = 0.9 +sell_value_min = 55 +sell_value_max = 110 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/trout_cutthroat/fish_trout_cutthroat.png b/fish/species/trout_cutthroat/fish_trout_cutthroat.png new file mode 100644 index 0000000..9e45417 Binary files /dev/null and b/fish/species/trout_cutthroat/fish_trout_cutthroat.png differ diff --git a/fish/species/trout_cutthroat/fish_trout_cutthroat.png.import b/fish/species/trout_cutthroat/fish_trout_cutthroat.png.import new file mode 100644 index 0000000..90a255e --- /dev/null +++ b/fish/species/trout_cutthroat/fish_trout_cutthroat.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nowggrl8wmr" +path="res://.godot/imported/fish_trout_cutthroat.png-a27813936c24b235f8eea97a7a7dfdc0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/trout_cutthroat/fish_trout_cutthroat.png" +dest_files=["res://.godot/imported/fish_trout_cutthroat.png-a27813936c24b235f8eea97a7a7dfdc0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/trout_cutthroat/trout_cutthroat.tres b/fish/species/trout_cutthroat/trout_cutthroat.tres new file mode 100644 index 0000000..fa29c47 --- /dev/null +++ b/fish/species/trout_cutthroat/trout_cutthroat.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://bd2t2ktvkkovd"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://nowggrl8wmr" path="res://fish/species/trout_cutthroat/fish_trout_cutthroat.png" id="4_texture"] + +[sub_resource type="Resource" id="TroutCutthroatAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"trout_cutthroat" +display_name = "cutthroat trout" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.4 +catch_profile = ExtResource("2_profile") +availability = SubResource("TroutCutthroatAvailability") +weight_min_lb = 0.25 +weight_max_lb = 10 +display_scale_min = 0.85 +display_scale_max = 1.4 +display_scale_curve = 0.9 +sell_value_min = 5 +sell_value_max = 12 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/trout_golden/fish_trout_golden.png b/fish/species/trout_golden/fish_trout_golden.png new file mode 100644 index 0000000..209b82d Binary files /dev/null and b/fish/species/trout_golden/fish_trout_golden.png differ diff --git a/fish/species/trout_golden/fish_trout_golden.png.import b/fish/species/trout_golden/fish_trout_golden.png.import new file mode 100644 index 0000000..bdff181 --- /dev/null +++ b/fish/species/trout_golden/fish_trout_golden.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b64w3kwf3wins" +path="res://.godot/imported/fish_trout_golden.png-00b8999ee94694397caec57987d8241d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/trout_golden/fish_trout_golden.png" +dest_files=["res://.godot/imported/fish_trout_golden.png-00b8999ee94694397caec57987d8241d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/trout_golden/trout_golden.tres b/fish/species/trout_golden/trout_golden.tres new file mode 100644 index 0000000..8aa3201 --- /dev/null +++ b/fish/species/trout_golden/trout_golden.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://n3ixatlsfp8y"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://b64w3kwf3wins" path="res://fish/species/trout_golden/fish_trout_golden.png" id="4_texture"] + +[sub_resource type="Resource" id="TroutGoldenAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"trout_golden" +display_name = "golden trout" +allowed_water_types = 1 +rarity = 2 +base_catch_weight = 0.2 +catch_profile = ExtResource("2_profile") +availability = SubResource("TroutGoldenAvailability") +weight_min_lb = 0.2 +weight_max_lb = 3 +display_scale_min = 0.85 +display_scale_curve = 0.9 +sell_value_min = 8 +sell_value_max = 16 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/trout_rainbow/fish_trout_rainbow.png b/fish/species/trout_rainbow/fish_trout_rainbow.png new file mode 100644 index 0000000..ff6fd06 Binary files /dev/null and b/fish/species/trout_rainbow/fish_trout_rainbow.png differ diff --git a/fish/species/trout_rainbow/fish_trout_rainbow.png.import b/fish/species/trout_rainbow/fish_trout_rainbow.png.import new file mode 100644 index 0000000..fd8eafe --- /dev/null +++ b/fish/species/trout_rainbow/fish_trout_rainbow.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mfxehkoxea21" +path="res://.godot/imported/fish_trout_rainbow.png-e58603ff0df9519b81e6b0d0da55998c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/trout_rainbow/fish_trout_rainbow.png" +dest_files=["res://.godot/imported/fish_trout_rainbow.png-e58603ff0df9519b81e6b0d0da55998c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/trout_rainbow/trout_rainbow.tres b/fish/species/trout_rainbow/trout_rainbow.tres new file mode 100644 index 0000000..f9c74f6 --- /dev/null +++ b/fish/species/trout_rainbow/trout_rainbow.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://c3r8v522etgaq"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://mfxehkoxea21" path="res://fish/species/trout_rainbow/fish_trout_rainbow.png" id="4_texture"] + +[sub_resource type="Resource" id="TroutRainbowAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"trout_rainbow" +display_name = "rainbow trout" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.45 +catch_profile = ExtResource("2_profile") +availability = SubResource("TroutRainbowAvailability") +weight_max_lb = 20 +display_scale_min = 0.85 +display_scale_max = 1.4 +display_scale_curve = 0.9 +sell_value_min = 5 +sell_value_max = 11 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/trout_steelhead/fish_trout_steelhead.png b/fish/species/trout_steelhead/fish_trout_steelhead.png new file mode 100644 index 0000000..b2600e0 Binary files /dev/null and b/fish/species/trout_steelhead/fish_trout_steelhead.png differ diff --git a/fish/species/trout_steelhead/fish_trout_steelhead.png.import b/fish/species/trout_steelhead/fish_trout_steelhead.png.import new file mode 100644 index 0000000..3b3200e --- /dev/null +++ b/fish/species/trout_steelhead/fish_trout_steelhead.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cabbpc55sf2gw" +path="res://.godot/imported/fish_trout_steelhead.png-10d14e8a4754db88ccdaf7ffe4e4d9fe.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/trout_steelhead/fish_trout_steelhead.png" +dest_files=["res://.godot/imported/fish_trout_steelhead.png-10d14e8a4754db88ccdaf7ffe4e4d9fe.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/trout_steelhead/trout_steelhead.tres b/fish/species/trout_steelhead/trout_steelhead.tres new file mode 100644 index 0000000..57887f7 --- /dev/null +++ b/fish/species/trout_steelhead/trout_steelhead.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://dv2hjtqd5xhfu"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://cabbpc55sf2gw" path="res://fish/species/trout_steelhead/fish_trout_steelhead.png" id="4_texture"] + +[sub_resource type="Resource" id="TroutSteelheadAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"trout_steelhead" +display_name = "steelhead trout" +allowed_water_types = 1 +rarity = 2 +base_catch_weight = 0.2 +catch_profile = ExtResource("2_profile") +availability = SubResource("TroutSteelheadAvailability") +weight_min_lb = 2 +weight_max_lb = 35 +display_scale_min = 0.85 +display_scale_max = 1.8 +display_scale_curve = 0.9 +sell_value_min = 10 +sell_value_max = 20 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/tuna_albacore/fish_tuna_albacore.png b/fish/species/tuna_albacore/fish_tuna_albacore.png new file mode 100644 index 0000000..3769c08 Binary files /dev/null and b/fish/species/tuna_albacore/fish_tuna_albacore.png differ diff --git a/fish/species/tuna_albacore/fish_tuna_albacore.png.import b/fish/species/tuna_albacore/fish_tuna_albacore.png.import new file mode 100644 index 0000000..f2e6b6f --- /dev/null +++ b/fish/species/tuna_albacore/fish_tuna_albacore.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxebn81i552fg" +path="res://.godot/imported/fish_tuna_albacore.png-aaac5d00c8a250b19db99d7069f549e1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/tuna_albacore/fish_tuna_albacore.png" +dest_files=["res://.godot/imported/fish_tuna_albacore.png-aaac5d00c8a250b19db99d7069f549e1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/tuna_albacore/tuna_albacore.tres b/fish/species/tuna_albacore/tuna_albacore.tres new file mode 100644 index 0000000..21a9155 --- /dev/null +++ b/fish/species/tuna_albacore/tuna_albacore.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://cjd2mibei3713"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bxebn81i552fg" path="res://fish/species/tuna_albacore/fish_tuna_albacore.png" id="4_texture"] + +[sub_resource type="Resource" id="AlbacoreAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"tuna_albacore" +display_name = "albacore tuna" +allowed_water_types = 2 +rarity = 2 +base_catch_weight = 1.5 +catch_profile = ExtResource("2_profile") +availability = SubResource("AlbacoreAvailability") +weight_min_lb = 10.0 +weight_max_lb = 45.0 +display_scale_min = 1.1 +display_scale_max = 1.9 +display_scale_curve = 0.85 +sell_value_min = 12 +sell_value_max = 24 +sell_value_curve = 0.85 +display_texture = ExtResource("4_texture") diff --git a/fish/species/tuna_bigeye/fish_tuna_bigeye.png b/fish/species/tuna_bigeye/fish_tuna_bigeye.png new file mode 100644 index 0000000..b4a36c3 Binary files /dev/null and b/fish/species/tuna_bigeye/fish_tuna_bigeye.png differ diff --git a/fish/species/tuna_bigeye/fish_tuna_bigeye.png.import b/fish/species/tuna_bigeye/fish_tuna_bigeye.png.import new file mode 100644 index 0000000..3539d10 --- /dev/null +++ b/fish/species/tuna_bigeye/fish_tuna_bigeye.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bypnhqmkhnm2c" +path="res://.godot/imported/fish_tuna_bigeye.png-1c20be325f0bac055325a6fa0ad17dd1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/tuna_bigeye/fish_tuna_bigeye.png" +dest_files=["res://.godot/imported/fish_tuna_bigeye.png-1c20be325f0bac055325a6fa0ad17dd1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/tuna_bigeye/tuna_bigeye.tres b/fish/species/tuna_bigeye/tuna_bigeye.tres new file mode 100644 index 0000000..e5e4b7c --- /dev/null +++ b/fish/species/tuna_bigeye/tuna_bigeye.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://mxa5d130i5oh"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bypnhqmkhnm2c" path="res://fish/species/tuna_bigeye/fish_tuna_bigeye.png" id="4_texture"] + +[sub_resource type="Resource" id="BigeyeAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"tuna_bigeye" +display_name = "bigeye tuna" +allowed_water_types = 2 +rarity = 3 +base_catch_weight = 0.55 +catch_profile = ExtResource("2_profile") +availability = SubResource("BigeyeAvailability") +weight_min_lb = 20.0 +weight_max_lb = 120.0 +display_scale_min = 1.2 +display_scale_max = 2.2 +display_scale_curve = 0.8 +sell_value_min = 22 +sell_value_max = 48 +sell_value_curve = 0.8 +display_texture = ExtResource("4_texture") diff --git a/fish/species/tuna_bluefin/fish_tuna_bluefin.png b/fish/species/tuna_bluefin/fish_tuna_bluefin.png new file mode 100644 index 0000000..a2b3cad Binary files /dev/null and b/fish/species/tuna_bluefin/fish_tuna_bluefin.png differ diff --git a/fish/species/tuna_bluefin/fish_tuna_bluefin.png.import b/fish/species/tuna_bluefin/fish_tuna_bluefin.png.import new file mode 100644 index 0000000..674a0b7 --- /dev/null +++ b/fish/species/tuna_bluefin/fish_tuna_bluefin.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dg7nafydp4flx" +path="res://.godot/imported/fish_tuna_bluefin.png-04ec876b3a78ea716b7a0673e8497fab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/tuna_bluefin/fish_tuna_bluefin.png" +dest_files=["res://.godot/imported/fish_tuna_bluefin.png-04ec876b3a78ea716b7a0673e8497fab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/tuna_bluefin/tuna_bluefin.tres b/fish/species/tuna_bluefin/tuna_bluefin.tres new file mode 100644 index 0000000..a669b5d --- /dev/null +++ b/fish/species/tuna_bluefin/tuna_bluefin.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://bkck5gncu7tvc"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://dg7nafydp4flx" path="res://fish/species/tuna_bluefin/fish_tuna_bluefin.png" id="4_texture"] + +[sub_resource type="Resource" id="BluefinAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"tuna_bluefin" +display_name = "bluefin tuna" +allowed_water_types = 2 +rarity = 4 +base_catch_weight = 0.18 +catch_profile = ExtResource("2_profile") +availability = SubResource("BluefinAvailability") +weight_min_lb = 30.0 +weight_max_lb = 250.0 +display_scale_min = 1.3 +display_scale_max = 2.5 +display_scale_curve = 0.78 +sell_value_min = 35 +sell_value_max = 90 +sell_value_curve = 0.78 +display_texture = ExtResource("4_texture") diff --git a/fish/species/tuna_skipjack/fish_tuna_skipjack.png b/fish/species/tuna_skipjack/fish_tuna_skipjack.png new file mode 100644 index 0000000..5a7f74d Binary files /dev/null and b/fish/species/tuna_skipjack/fish_tuna_skipjack.png differ diff --git a/fish/species/tuna_skipjack/fish_tuna_skipjack.png.import b/fish/species/tuna_skipjack/fish_tuna_skipjack.png.import new file mode 100644 index 0000000..786e985 --- /dev/null +++ b/fish/species/tuna_skipjack/fish_tuna_skipjack.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vvtr81d2aojs" +path="res://.godot/imported/fish_tuna_skipjack.png-98d7d6a76fc86bd76f861358be0635c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/tuna_skipjack/fish_tuna_skipjack.png" +dest_files=["res://.godot/imported/fish_tuna_skipjack.png-98d7d6a76fc86bd76f861358be0635c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/tuna_skipjack/tuna_skipjack.tres b/fish/species/tuna_skipjack/tuna_skipjack.tres new file mode 100644 index 0000000..4d9e5a8 --- /dev/null +++ b/fish/species/tuna_skipjack/tuna_skipjack.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://dli584n85vpfi"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://vvtr81d2aojs" path="res://fish/species/tuna_skipjack/fish_tuna_skipjack.png" id="4_texture"] + +[sub_resource type="Resource" id="SkipjackAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"tuna_skipjack" +display_name = "skipjack tuna" +allowed_water_types = 2 +rarity = 1 +base_catch_weight = 3.0 +catch_profile = ExtResource("2_profile") +availability = SubResource("SkipjackAvailability") +weight_min_lb = 5.0 +weight_max_lb = 25.0 +display_scale_min = 1.0 +display_scale_max = 1.7 +display_scale_curve = 0.9 +sell_value_min = 8 +sell_value_max = 16 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png new file mode 100644 index 0000000..a1afc81 Binary files /dev/null and b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png differ diff --git a/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png.import b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png.import new file mode 100644 index 0000000..a8918aa --- /dev/null +++ b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwpdgagaysdsf" +path="res://.godot/imported/fish_tuna_yellowfin.png-14cd53683bfec6af599f1e1dc5728b99.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/tuna_yellowfin/fish_tuna_yellowfin.png" +dest_files=["res://.godot/imported/fish_tuna_yellowfin.png-14cd53683bfec6af599f1e1dc5728b99.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/tuna_yellowfin/tuna_yellowfin.tres b/fish/species/tuna_yellowfin/tuna_yellowfin.tres new file mode 100644 index 0000000..cbd4950 --- /dev/null +++ b/fish/species/tuna_yellowfin/tuna_yellowfin.tres @@ -0,0 +1,29 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://yjeoqfht5ql1"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://bwpdgagaysdsf" path="res://fish/species/tuna_yellowfin/fish_tuna_yellowfin.png" id="4_texture"] + +[sub_resource type="Resource" id="YellowfinAvailability"] +script = ExtResource("3_availability_script") +preferred_bait_tags = Array[StringName]([&"minnow"]) +preferred_bait_weight_multiplier = 1.4 + +[resource] +script = ExtResource("1_fish_data") +id = &"tuna_yellowfin" +display_name = "yellowfin tuna" +allowed_water_types = 2 +rarity = 2 +catch_profile = ExtResource("2_profile") +availability = SubResource("YellowfinAvailability") +weight_min_lb = 15.0 +weight_max_lb = 80.0 +display_scale_min = 1.15 +display_scale_max = 2.1 +display_scale_curve = 0.82 +sell_value_min = 16 +sell_value_max = 36 +sell_value_curve = 0.82 +display_texture = ExtResource("4_texture") diff --git a/fish/species/walleye/fish_walleye.png b/fish/species/walleye/fish_walleye.png new file mode 100644 index 0000000..ed3eb96 Binary files /dev/null and b/fish/species/walleye/fish_walleye.png differ diff --git a/fish/species/walleye/fish_walleye.png.import b/fish/species/walleye/fish_walleye.png.import new file mode 100644 index 0000000..21e025e --- /dev/null +++ b/fish/species/walleye/fish_walleye.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c10j6jv6fv32r" +path="res://.godot/imported/fish_walleye.png-48ef5c2e01ff8de0726161700ea41c43.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://fish/species/walleye/fish_walleye.png" +dest_files=["res://.godot/imported/fish_walleye.png-48ef5c2e01ff8de0726161700ea41c43.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/fish/species/walleye/walleye.tres b/fish/species/walleye/walleye.tres new file mode 100644 index 0000000..ce833c9 --- /dev/null +++ b/fish/species/walleye/walleye.tres @@ -0,0 +1,28 @@ +[gd_resource type="Resource" script_class="FishData" format=3 uid="uid://8j0f5dchtajt"] + +[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"] +[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] +[ext_resource type="Script" uid="uid://bs7cqi88csolc" path="res://fish/fish_availability.gd" id="3_availability_script"] +[ext_resource type="Texture2D" uid="uid://c10j6jv6fv32r" path="res://fish/species/walleye/fish_walleye.png" id="4_texture"] + +[sub_resource type="Resource" id="WalleyeAvailability"] +script = ExtResource("3_availability_script") +allowed_location_tags = Array[StringName]([&"starter_pond"]) + +[resource] +script = ExtResource("1_fish_data") +id = &"walleye" +display_name = "walleye" +allowed_water_types = 1 +rarity = 1 +base_catch_weight = 0.4 +catch_profile = ExtResource("2_profile") +availability = SubResource("WalleyeAvailability") +weight_max_lb = 15 +display_scale_min = 0.85 +display_scale_max = 1.7 +display_scale_curve = 0.9 +sell_value_min = 7 +sell_value_max = 15 +sell_value_curve = 0.9 +display_texture = ExtResource("4_texture") diff --git a/fishing/bass_catch_profile.tres b/fishing/bass_catch_profile.tres index 077b229..24024a7 100644 --- a/fishing/bass_catch_profile.tres +++ b/fishing/bass_catch_profile.tres @@ -6,11 +6,6 @@ script = ExtResource("1_profile") barrier_count_min = 3 barrier_count_max = 4 -barrier_health_min = 4 -barrier_health_max = 7 first_barrier_margin = 0.14 final_barrier_margin = 0.1 minimum_barrier_spacing = 0.14 -chase_start_delay = 1.0 -chase_start_offset = 0.16 -chase_speed = 0.145 diff --git a/fishing/carp_catch_profile.tres b/fishing/carp_catch_profile.tres index bc3f5dc..e1e0efe 100644 --- a/fishing/carp_catch_profile.tres +++ b/fishing/carp_catch_profile.tres @@ -6,11 +6,6 @@ script = ExtResource("1_profile") barrier_count_min = 3 barrier_count_max = 4 -barrier_health_min = 5 -barrier_health_max = 8 first_barrier_margin = 0.13 final_barrier_margin = 0.09 minimum_barrier_spacing = 0.14 -chase_start_delay = 1.1 -chase_start_offset = 0.18 -chase_speed = 0.15 diff --git a/fishing/catch_controller.gd b/fishing/catch_controller.gd index 53617df..7e75370 100644 --- a/fishing/catch_controller.gd +++ b/fishing/catch_controller.gd @@ -4,6 +4,7 @@ extends Node const CatchDifficultyProfileType = preload( "res://fishing/catch_difficulty_profile.gd" ) +const FishQualityType = preload("res://fish/fish_quality.gd") signal encounter_updated( progress: float, @@ -42,6 +43,13 @@ class Barrier: maximum_health = barrier_health +# Fight movement uses one shared baseline. Species and quality difficulty comes +# from barrier placement and health, while reel upgrades affect only the +# player's progress speed. +const CHASE_START_DELAY: float = 1.0 +const CHASE_START_OFFSET: float = 0.04 +const CHASE_SPEED: float = 0.07 + @export_category("Accessibility") @export var auto_click_enabled: bool = false @export_range(0.05, 2.0, 0.01) var auto_click_interval: float = 0.20 @@ -60,9 +68,8 @@ var _active_barrier_index: int = -1 var _reel_input_held: bool = false var _reel_speed: float = 0.0 var _click_power: int = 1 -var _chase_start_delay: float = 0.0 +var _fish_quality: int = FishQualityType.Tier.BORING var _chase_delay_remaining: float = 0.0 -var _chase_speed: float = 0.0 var _failure_epsilon: float = 0.0001 var _auto_click_accumulator: float = 0.0 var _rng: RandomNumberGenerator = RandomNumberGenerator.new() @@ -103,6 +110,7 @@ func start_encounter( profile: CatchDifficultyProfileType, reel_speed: float, click_power: int, + fish_quality: int = FishQualityType.Tier.BORING, ) -> void: reset() if profile == null: @@ -111,10 +119,13 @@ func start_encounter( _reel_speed = maxf(reel_speed, 0.0) _click_power = maxi(click_power, 1) - _chase_start_delay = maxf(profile.chase_start_delay, 0.0) - _chase_delay_remaining = _chase_start_delay - chase_progress = -maxf(profile.chase_start_offset, 0.01) - _chase_speed = maxf(profile.chase_speed, 0.0) + _fish_quality = ( + fish_quality + if FishQualityType.is_valid(fish_quality) + else FishQualityType.Tier.BORING + ) + _chase_delay_remaining = CHASE_START_DELAY + chase_progress = -CHASE_START_OFFSET _seed_encounter_rng() _generate_barriers(profile) progress = 0.0 @@ -126,13 +137,19 @@ func start_authoritative_encounter( profile: CatchDifficultyProfileType, reel_speed: float, click_power: int, - seed: int, + encounter_seed_value: int, + fish_quality: int = FishQualityType.Tier.BORING, ) -> void: var previous_test_mode: bool = use_deterministic_test_seed var previous_seed: int = deterministic_test_seed use_deterministic_test_seed = true - deterministic_test_seed = seed - start_encounter(profile, reel_speed, click_power) + deterministic_test_seed = encounter_seed_value + start_encounter( + profile, + reel_speed, + click_power, + fish_quality, + ) use_deterministic_test_seed = previous_test_mode deterministic_test_seed = previous_seed @@ -168,9 +185,8 @@ func reset() -> void: _reel_input_held = false _reel_speed = 0.0 _click_power = 1 - _chase_start_delay = 0.0 + _fish_quality = FishQualityType.Tier.BORING _chase_delay_remaining = 0.0 - _chase_speed = 0.0 _auto_click_accumulator = 0.0 emit_signal( "encounter_updated", @@ -215,7 +231,7 @@ func _update_chase(delta: float) -> void: if active_delta <= 0.0: return chase_progress = minf( - chase_progress + _chase_speed * active_delta, + chase_progress + CHASE_SPEED * active_delta, 1.0 ) @@ -278,7 +294,6 @@ func _seed_encounter_rng() -> void: func _generate_barriers(profile: CatchDifficultyProfileType) -> void: var count_range: Vector2i = profile.get_barrier_count_range() - var health_range: Vector2i = profile.get_barrier_health_range() var interval: Vector2 = profile.get_generation_interval() var spacing: float = maxf(profile.minimum_barrier_spacing, 0.01) var available_distance: float = maxf(interval.y - interval.x, 0.0) @@ -302,7 +317,9 @@ func _generate_barriers(profile: CatchDifficultyProfileType) -> void: + spacing * float(barrier_index) + random_offsets[barrier_index] * random_slack ) - var health: int = _rng.randi_range(health_range.x, health_range.y) + var health: int = FishQualityType.roll_barrier_health( + _rng, _fish_quality + ) _barriers.append(Barrier.new(position, health)) diff --git a/fishing/catch_difficulty_profile.gd b/fishing/catch_difficulty_profile.gd index 3ad20ad..60aa190 100644 --- a/fishing/catch_difficulty_profile.gd +++ b/fishing/catch_difficulty_profile.gd @@ -3,14 +3,9 @@ extends Resource @export_range(0, 12, 1) var barrier_count_min: int = 2 @export_range(0, 12, 1) var barrier_count_max: int = 4 -@export_range(1, 100, 1) var barrier_health_min: int = 3 -@export_range(1, 100, 1) var barrier_health_max: int = 7 @export_range(0.0, 0.45, 0.01) var first_barrier_margin: float = 0.15 @export_range(0.0, 0.45, 0.01) var final_barrier_margin: float = 0.10 @export_range(0.01, 1.0, 0.01) var minimum_barrier_spacing: float = 0.15 -@export_range(0.0, 5.0, 0.05) var chase_start_delay: float = 1.0 -@export_range(0.01, 0.5, 0.01) var chase_start_offset: float = 0.15 -@export_range(0.0, 2.0, 0.01) var chase_speed: float = 0.14 func get_barrier_count_range() -> Vector2i: @@ -18,11 +13,6 @@ func get_barrier_count_range() -> Vector2i: return Vector2i(minimum, maxi(minimum, barrier_count_max)) -func get_barrier_health_range() -> Vector2i: - var minimum: int = maxi(1, barrier_health_min) - return Vector2i(minimum, maxi(minimum, barrier_health_max)) - - func get_generation_interval() -> Vector2: var start: float = clampf(first_barrier_margin, 0.0, 0.95) var finish: float = clampf(1.0 - final_barrier_margin, start, 1.0) diff --git a/fishing/common_catch_profile.tres b/fishing/common_catch_profile.tres index 1169e2e..86c2b74 100644 --- a/fishing/common_catch_profile.tres +++ b/fishing/common_catch_profile.tres @@ -6,11 +6,6 @@ script = ExtResource("1_profile") barrier_count_min = 2 barrier_count_max = 4 -barrier_health_min = 3 -barrier_health_max = 7 first_barrier_margin = 0.15 final_barrier_margin = 0.1 minimum_barrier_spacing = 0.15 -chase_start_delay = 1.0 -chase_start_offset = 0.15 -chase_speed = 0.14 diff --git a/fishing/fishing_presentation.gd b/fishing/fishing_presentation.gd index 09ed554..71fedac 100644 --- a/fishing/fishing_presentation.gd +++ b/fishing/fishing_presentation.gd @@ -1,6 +1,10 @@ class_name FishingPresentation extends Node3D +const WaterSurfaceMotionType = preload( + "res://world/water_surface_motion.gd" +) + signal cast_completed signal outcome_completed(outcome: StringName) signal presentation_interrupted @@ -25,6 +29,7 @@ enum LineMode { @export_range(0.0, 3.0, 0.01) var maximum_slack: float = 0.65 @export_range(2, 20, 1) var sag_interpolation_count: int = 8 @export_range(0.1, 30.0, 0.1) var line_transition_speed: float = 7.0 +@export_range(0.002, 0.05, 0.001) var line_thickness: float = 0.016 @export_category("Cast") @export_range(0.1, 3.0, 0.05) var cast_travel_duration: float = 0.65 @@ -52,8 +57,11 @@ var _rod: Node3D var _rod_tip: Marker3D var _rod_neutral_rotation: Vector3 var _cast_arrival_position: Vector3 +var _active_cast_arc_height: float = 0.0 +var _cast_tracks_water_surface: bool = false var _bobber_surface_position: Vector3 var _bobber_idle_elapsed: float = 0.0 +var _bobber_base_scale: Vector3 = Vector3.ONE var _active_tween: Tween var _rod_tween: Tween var _bite_tween: Tween @@ -68,6 +76,9 @@ func _process(delta: float) -> void: if _mode == VisualMode.FISHING and _bobber.visible: _bobber_idle_elapsed += delta _apply_bobber_idle_motion() + var bobber_position := _bobber.global_position + bobber_position.y += WaterSurfaceMotionType.get_default_height_offset() + _bobber.global_position = bobber_position if _line_mode != LineMode.HIDDEN: _update_line(delta) @@ -86,11 +97,13 @@ func begin_aim( minimum_target: Vector3, target_normal: Vector3, target_is_fishable: bool, + character_scale: float = 1.0, ) -> void: cleanup() if rod_tip == null or rod == null: return + _bobber_base_scale = Vector3.ONE * maxf(character_scale, 0.01) _mode = VisualMode.AIMING _rod = rod _rod_tip = rod_tip @@ -142,6 +155,7 @@ func begin_cast( target: Vector3, blocked_landing_position: Vector3 = Vector3.ZERO, cast_is_blocked: bool = false, + resolved_arc_height: float = -1.0, ) -> void: if _mode != VisualMode.AIMING or _rod_tip == null: return @@ -150,10 +164,16 @@ func begin_cast( _mode = VisualMode.CASTING _target_marker.visible = false _bobber.visible = true - _bobber.scale = Vector3.ONE + _bobber.scale = _bobber_base_scale _cast_arrival_position = ( blocked_landing_position if cast_is_blocked else target ) + _active_cast_arc_height = ( + maxf(resolved_arc_height, 0.0) + if resolved_arc_height >= 0.0 + else cast_arc_height + ) + _cast_tracks_water_surface = not cast_is_blocked var cast_start: Vector3 = _rod_tip.global_position _bobber.global_position = cast_start @@ -179,26 +199,26 @@ func show_bite() -> void: return _kill_bite_tween() - _bobber.scale = Vector3.ONE + _bobber.scale = _bobber_base_scale _bite_tween = create_tween() _bite_tween.set_trans(Tween.TRANS_QUAD) _bite_tween.set_ease(Tween.EASE_IN_OUT) _bite_tween.tween_property( _bobber, "scale", - Vector3.ONE * 0.68, + _bobber_base_scale * 0.68, 0.08 ) _bite_tween.tween_property( _bobber, "scale", - Vector3.ONE * 1.2, + _bobber_base_scale * 1.2, 0.1 ) _bite_tween.tween_property( _bobber, "scale", - Vector3.ONE, + _bobber_base_scale, 0.1 ) _bite_tween.finished.connect(_on_bite_tween_finished) @@ -238,7 +258,7 @@ func play_outcome(outcome: StringName) -> void: # Withdrawal is a visible return, not an instant cancellation. Keep the # bobber present even if the preceding reel update hid or reset it. _bobber.visible = true - _bobber.scale = Vector3.ONE + _bobber.scale = _bobber_base_scale _active_tween = create_tween() _active_tween.set_trans(Tween.TRANS_QUAD) _active_tween.set_ease(Tween.EASE_IN) @@ -287,8 +307,11 @@ func cleanup() -> void: _invalid_target_marker.visible = false _bobber.visible = false _bobber.scale = Vector3.ONE + _bobber_base_scale = Vector3.ONE _bobber.rotation = Vector3.ZERO _cast_arrival_position = Vector3.ZERO + _active_cast_arc_height = 0.0 + _cast_tracks_water_surface = false _bobber_surface_position = Vector3.ZERO _bobber_idle_elapsed = 0.0 set_line_mode(LineMode.HIDDEN) @@ -305,7 +328,12 @@ func _set_cast_sample( target: Vector3, ) -> void: var sample: Vector3 = start.lerp(target, progress) - sample.y += sin(progress * PI) * cast_arc_height + sample.y += sin(progress * PI) * _active_cast_arc_height + if _cast_tracks_water_surface: + sample.y += ( + WaterSurfaceMotionType.get_default_height_offset() + * smoothstep(0.72, 1.0, progress) + ) _bobber.global_position = sample @@ -390,10 +418,51 @@ func _update_line(delta: float) -> void: _line_mesh.clear_surfaces() if rendered_points.size() < 2: return - _line_mesh.surface_begin(Mesh.PRIMITIVE_LINES) - for point_index: int in range(1, rendered_points.size()): - _line_mesh.surface_add_vertex(to_local(rendered_points[point_index - 1])) - _line_mesh.surface_add_vertex(to_local(rendered_points[point_index])) + _draw_line_ribbon(rendered_points) + + +func _draw_line_ribbon(points: PackedVector3Array) -> void: + var camera: Camera3D = get_viewport().get_camera_3d() + if camera == null: + _line_mesh.surface_begin(Mesh.PRIMITIVE_LINE_STRIP) + for point: Vector3 in points: + _line_mesh.surface_add_vertex(to_local(point)) + _line_mesh.surface_end() + return + + var half_thickness: float = line_thickness * 0.5 + var ribbon_vertices := PackedVector3Array() + for point_index: int in range(1, points.size()): + var start: Vector3 = points[point_index - 1] + var end: Vector3 = points[point_index] + var segment: Vector3 = end - start + if segment.length_squared() <= 0.000001: + continue + var to_camera: Vector3 = ( + camera.global_position - start.lerp(end, 0.5) + ) + var side: Vector3 = segment.normalized().cross(to_camera.normalized()) + if side.length_squared() <= 0.000001: + side = segment.normalized().cross(Vector3.UP) + if side.length_squared() <= 0.000001: + side = segment.normalized().cross(Vector3.RIGHT) + side = side.normalized() * half_thickness + + var start_left: Vector3 = to_local(start - side) + var start_right: Vector3 = to_local(start + side) + var end_right: Vector3 = to_local(end + side) + var end_left: Vector3 = to_local(end - side) + ribbon_vertices.append(start_left) + ribbon_vertices.append(start_right) + ribbon_vertices.append(end_right) + ribbon_vertices.append(start_left) + ribbon_vertices.append(end_right) + ribbon_vertices.append(end_left) + if ribbon_vertices.is_empty(): + return + _line_mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) + for vertex: Vector3 in ribbon_vertices: + _line_mesh.surface_add_vertex(vertex) _line_mesh.surface_end() @@ -460,7 +529,7 @@ func _kill_bite_tween() -> void: _bite_tween.kill() _bite_tween = null if is_instance_valid(_bobber): - _bobber.scale = Vector3.ONE + _bobber.scale = _bobber_base_scale func _on_bite_tween_finished() -> void: diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd index c694461..167a08b 100644 --- a/fishing/fishing_spot.gd +++ b/fishing/fishing_spot.gd @@ -47,6 +47,7 @@ const WorldWeatherServiceType = preload( ) signal status_changed(status: String) +signal local_speech_requested(message: String) signal catch_display_changed( progress: float, chase_progress: float, @@ -64,9 +65,8 @@ signal showcase_changed( visible: bool, ) signal bite_activated +signal bite_prompt_changed(is_visible: bool) signal ready_for_equipment_refresh -signal fish_showcase_toggle_requested -signal art_ui_toggle_requested enum FishingState { READY, @@ -95,19 +95,19 @@ enum FishingState { @export_range(0.01, 0.5, 0.01) var solid_bobber_clearance: float = 0.13 @export_category("Withdrawal") -@export_range(0.1, 20.0, 0.1) var withdrawal_rate: float = 4.9 +@export_range(0.1, 20.0, 0.1) var withdrawal_rate: float = 2.45 @export_range(0.1, 10.0, 0.1) var withdrawal_cancel_distance: float = 1.0 @export_range(0.0, 1.0, 0.01) var withdrawal_surface_clearance: float = 0.4 @export_category("Timing") -const BITE_QUICK_MIN_SECONDS: float = 10.0 -const BITE_QUICK_MAX_SECONDS: float = 30.0 -const BITE_TYPICAL_MAX_SECONDS: float = 90.0 -const BITE_LONG_MAX_SECONDS: float = 180.0 -const BITE_MAX_SECONDS: float = 240.0 -const BITE_QUICK_PROBABILITY: float = 0.25 +const BITE_QUICK_MIN_SECONDS: float = 6.0 +const BITE_QUICK_MAX_SECONDS: float = 18.0 +const BITE_TYPICAL_MAX_SECONDS: float = 48.0 +const BITE_LONG_MAX_SECONDS: float = 72.0 +const BITE_MAX_SECONDS: float = 90.0 +const BITE_QUICK_PROBABILITY: float = 0.30 const BITE_TYPICAL_PROBABILITY: float = 0.65 -const BITE_LONG_PROBABILITY: float = 0.08 +const BITE_LONG_PROBABILITY: float = 0.04 const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1 @export_range(0.1, 10.0, 0.1) var cooldown_duration: float = 1.0 @@ -122,6 +122,9 @@ const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1 @onready var _catch_controller: CatchControllerType = %CatchController @onready var _presentation: FishingPresentationType = %FishingPresentation +@onready var _bobber_audio: AudioStreamPlayer = %BobberAudio +@onready var _fight_audio: AudioStreamPlayer = %FightAudio +@onready var _reeling_audio: AudioStreamPlayer = %ReelingAudio var state: FishingState = FishingState.READY var _external_input_blocked: bool = false @@ -148,6 +151,7 @@ var _cast_charge: float = 0.0 var _cast_direction: Vector3 = Vector3.FORWARD var _cast_origin_position: Vector3 var _cast_target: Vector3 +var _preferred_cast_arc_height: float = -1.0 var _withdrawal_endpoint: Vector3 var _withdrawal_progress: float = 0.0 var _withdrawal_input_held: bool = false @@ -172,11 +176,19 @@ var _network_auto_click_accumulator: float = 0.0 var _network_active_barrier_index: int = -1 var _bite_rng: RandomNumberGenerator = RandomNumberGenerator.new() var _pending_cleanup_message: String = "" +var _bite_confirmation_pending: bool = false +var _bite_confirmation_requested: bool = false func _ready() -> void: _bite_rng.randomize() _configure_surface_resolver() + var fight_stream := _fight_audio.stream as AudioStreamWAV + if fight_stream != null: + _configure_audio_loop(fight_stream) + var reeling_stream := _reeling_audio.stream as AudioStreamWAV + if reeling_stream != null: + _configure_audio_loop(reeling_stream) _catch_controller.encounter_updated.connect(_on_catch_encounter_updated) _catch_controller.caught.connect(_on_catch_completed) _catch_controller.escaped.connect(_on_catch_escaped) @@ -223,6 +235,15 @@ func setup( _network_fishing = network_fishing _world_time = world_time _world_weather = world_weather + if ( + _network_session != null + and not _network_session.state_changed.is_connected( + _on_network_session_state_changed + ) + ): + _network_session.state_changed.connect( + _on_network_session_state_changed + ) if _network_fishing != null: _network_fishing.local_cast_accepted.connect( _on_network_cast_accepted @@ -233,6 +254,9 @@ func setup( _network_fishing.local_bite_started.connect( _on_network_bite_started ) + _network_fishing.local_bite_pending.connect( + _on_network_bite_pending + ) _network_fishing.local_snapshot_received.connect( _on_network_fishing_snapshot ) @@ -337,17 +361,18 @@ func handle_escape_action() -> bool: func set_local_menu_input_suppressed( - owner: StringName, + input_owner: StringName, suppressed: bool, ) -> void: - if owner.is_empty(): + if input_owner.is_empty(): return if suppressed: - _local_menu_input_owners[owner] = true + _local_menu_input_owners[input_owner] = true else: - _local_menu_input_owners.erase(owner) + _local_menu_input_owners.erase(input_owner) if suppressed and state == FishingState.WAITING_FOR_BITE: _withdrawal_input_held = false + _stop_reeling_audio() _presentation.set_line_mode( FishingPresentationType.LineMode.SLACK ) @@ -357,6 +382,7 @@ func set_gameplay_input_enabled(enabled: bool) -> void: _gameplay_input_enabled = enabled if not enabled: _local_menu_input_owners.clear() + _stop_reeling_audio() func configure_accessibility_auto_click( @@ -375,6 +401,11 @@ func begin_water_recovery() -> void: FishingState.FIGHTING, ]: _cancel_attempt() + # Recovery replaces the normal rod-return presentation. Finish the local + # cleanup now so its temporary movement lock is not captured and restored + # after the player respawns. + if state != FishingState.READY: + _finalize_attempt_cleanup("") elif state == FishingState.SHOWING_CATCH: _secure_showcase_catch_for_recovery() @@ -400,6 +431,8 @@ func _secure_showcase_catch_for_recovery() -> void: func _exit_tree() -> void: + _stop_fight_audio() + _stop_reeling_audio() _showcase_restore_generation += 1 if ( state == FishingState.SHOWING_CATCH @@ -475,31 +508,28 @@ func _unhandled_input(event: InputEvent) -> void: FishingState.READY: if not _new_cast_press_armed: return - if ( - _local_hotbar != null - and not _local_hotbar.get_selected_fish_catch_id().is_empty() - ): - fish_showcase_toggle_requested.emit() - get_viewport().set_input_as_handled() - return var active_item: ItemDataType = _get_active_item() if ( active_item != null - and active_item.item_id == ArtShopStockType.ART_KIT_ITEM_ID - ): - art_ui_toggle_requested.emit() - get_viewport().set_input_as_handled() - return - if ( - active_item != null - and active_item.category == ItemDataType.Category.CONSUMABLE + and ( + active_item.category == ItemDataType.Category.CONSUMABLE + or ( + active_item.item_id == PlayerItemEffectsType.FISH_FINDER_ID + ) + ) ): if _network_item_use != null: _network_item_use.request_use(active_item.item_id) - elif _item_effects.use_consumable(active_item, _local_bag): + elif _item_effects.use_item(active_item, _local_bag): status_changed.emit(_item_effects.get_feedback( active_item.item_id )) + elif ( + active_item.item_id == PlayerItemEffectsType.FISH_FINDER_ID + ): + local_speech_requested.emit( + PlayerItemEffectsType.FISH_FINDER_DEAD_MESSAGE + ) get_viewport().set_input_as_handled() return if not has_active_fishing_rod(): @@ -530,7 +560,12 @@ func _unhandled_input(event: InputEvent) -> void: else: return FishingState.WAITING_FOR_BITE: + if _bite_confirmation_pending: + confirm_pending_bite() + get_viewport().set_input_as_handled() + return _withdrawal_input_held = true + _start_reeling_audio() if _network_fishing != null: _network_primary_input_held = true _network_input_resend_elapsed = 0.0 @@ -545,7 +580,11 @@ func _unhandled_input(event: InputEvent) -> void: _confirm_cast() get_viewport().set_input_as_handled() elif state == FishingState.WAITING_FOR_BITE: + if _bite_confirmation_pending: + get_viewport().set_input_as_handled() + return _withdrawal_input_held = false + _stop_reeling_audio() if _network_fishing != null: _network_primary_input_held = false _network_input_resend_elapsed = 0.0 @@ -572,8 +611,12 @@ func _begin_aiming(player: PlayerType) -> void: _cast_charge = 0.0 _cast_target = _calculate_cast_target(minimum_cast_distance) state = FishingState.AIMING_CAST + _active_player.set_casting_visual() status_changed.emit("") - _cast_path_is_clear = is_cast_path_clear(_cast_origin_position, _cast_target) + _cast_path_is_clear = is_cast_path_clear( + _get_cast_launch_position(), + _cast_target, + ) var target_is_fishable: bool = ( _aim_surface_sample.is_fishable() and _cast_path_is_clear ) @@ -585,7 +628,8 @@ func _begin_aiming(player: PlayerType) -> void: _active_player.get_fishing_rod(), preview_position, _aim_surface_sample.normal, - target_is_fishable + target_is_fishable, + _active_player.get_character_visual_scale(), ) @@ -657,6 +701,28 @@ func is_fighting() -> bool: return state == FishingState.FIGHTING +func confirm_pending_bite() -> void: + if state != FishingState.WAITING_FOR_BITE or not _bite_confirmation_pending: + return + _bite_confirmation_requested = true + if _network_fishing != null: + _network_primary_input_held = false + _network_input_resend_elapsed = 0.0 + _network_fishing.submit_local_input(false, true) + else: + _activate_bite(true) + + +func _set_bite_confirmation_pending(is_pending: bool) -> void: + if _bite_confirmation_pending == is_pending: + if not is_pending: + _bite_confirmation_requested = false + return + _bite_confirmation_pending = is_pending + _bite_confirmation_requested = false + bite_prompt_changed.emit(is_pending) + + func is_returning() -> bool: return state == FishingState.RETURNING @@ -678,7 +744,10 @@ func _update_cast_charge(delta: float) -> void: var maximum_distance: float = maxf(minimum_cast_distance, maximum_cast_distance) var distance: float = lerpf(minimum_cast_distance, maximum_distance, _cast_charge) _cast_target = _calculate_cast_target(distance) - _cast_path_is_clear = is_cast_path_clear(_cast_origin_position, _cast_target) + _cast_path_is_clear = is_cast_path_clear( + _get_cast_launch_position(), + _cast_target, + ) var target_is_fishable: bool = ( _aim_surface_sample.is_fishable() and _cast_path_is_clear ) @@ -698,29 +767,45 @@ func _confirm_cast() -> void: if state != FishingState.AIMING_CAST: return - _cast_path_is_clear = is_cast_path_clear( - _cast_origin_position, + var cast_launch_position := _get_cast_launch_position() + var resolved_cast_arc: Dictionary = _surface_resolver.resolve_cast_arc( + get_world_3d().direct_space_state, + cast_launch_position, _cast_target, + _presentation.cast_arc_height, ) + var resolved_arc_height: float = float( + resolved_cast_arc.get("arc_height", _presentation.cast_arc_height) + ) + var resolved_cast_collision: Dictionary = resolved_cast_arc.get( + "collision", + {}, + ) + _cast_path_is_clear = resolved_cast_collision.is_empty() var cast_is_invalid: bool = ( not _aim_surface_sample.is_fishable() or not _cast_path_is_clear ) var arrival_position: Vector3 = _cast_target if not _cast_path_is_clear: - arrival_position = _resolve_cast_impact_position( - _cast_origin_position, + var collision_position: Variant = resolved_cast_collision.get( + "position", _cast_target, ) + if typeof(collision_position) == TYPE_VECTOR3: + arrival_position = collision_position if _active_player != null: # Movement is available while aiming, then locks once the cast is # released so the active bobber/fishing event remains anchored. _active_player.set_movement_enabled(false) state = FishingState.CASTING + if _active_player != null: + _active_player.set_release_visual() status_changed.emit("") _presentation.begin_cast( _cast_target, arrival_position, cast_is_invalid, + resolved_arc_height, ) _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) @@ -731,6 +816,7 @@ func _on_cast_completed() -> void: if not _is_cast_target_valid(_cast_target): _cleanup_attempt("", &"invalid") return + _play_bobber_audio() if _network_fishing != null: _network_fishing.request_local_cast( _cast_origin_position, @@ -762,8 +848,10 @@ func _on_cast_completed() -> void: if _selected_fish == null: _cleanup_attempt("nothing is biting here.", &"invalid") return + _consume_active_bait() state = FishingState.WAITING_FOR_BITE + _active_player.set_fishing_visual(true) _state_time_remaining = roll_bite_wait_time() * ( _item_effects.get_bite_time_multiplier() if _item_effects != null @@ -780,7 +868,14 @@ func _on_cast_completed() -> void: _cast_origin_position.z + _cast_direction.z * withdrawal_cancel_distance ) _presentation.set_line_mode(FishingPresentationType.LineMode.SLACK) - status_changed.emit("waiting for a bite...") + status_changed.emit("") + + +func _consume_active_bait() -> void: + if _active_player == null or _active_player.active_bait_id.is_empty(): + return + if _local_bag == null or not _local_bag.remove_item(_active_player.active_bait_id, 1): + _active_player.unequip_bait() func roll_bite_wait_time() -> float: @@ -811,11 +906,14 @@ func roll_bite_wait_time() -> float: func _update_waiting_for_bite(delta: float) -> void: + if _bite_confirmation_pending: + return if ( _withdrawal_input_held and not Input.is_action_pressed("fish_primary") ): _withdrawal_input_held = false + _stop_reeling_audio() _presentation.set_line_mode(FishingPresentationType.LineMode.SLACK) if not _withdrawal_input_held: @@ -904,10 +1002,11 @@ func _get_withdrawable_distance() -> float: return landing_offset.length() - withdrawal_cancel_distance -func _activate_bite() -> void: +func _activate_bite(confirmation_override: bool = false) -> void: if state != FishingState.WAITING_FOR_BITE: return + _stop_reeling_audio() if ( _active_player == null or _selected_fish == null @@ -915,10 +1014,28 @@ func _activate_bite() -> void: ): _cancel_attempt() return + if ( + not confirmation_override + and _active_lure_has_effect(&"deferred_fight") + ): + _state_time_remaining = 0.0 + _withdrawal_input_held = false + _set_bite_confirmation_pending(true) + status_changed.emit("") + _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) + _presentation.show_bite() + return + _set_bite_confirmation_pending(false) state = FishingState.FIGHTING + _active_player.set_fighting_visual(true) _state_time_remaining = 0.0 _withdrawal_input_held = false + _pending_catch = _fish_selector.create_catch(_selected_fish) + if _pending_catch == null or not _pending_catch.is_valid(): + _cancel_attempt() + return + _start_fight_audio() bite_activated.emit() status_changed.emit("fish on!") _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) @@ -926,7 +1043,8 @@ func _activate_bite() -> void: _catch_controller.start_encounter( _selected_fish.catch_profile, _get_effective_reel_speed(), - _get_effective_barrier_damage() + _get_effective_barrier_damage(), + _pending_catch.quality, ) _catch_controller.set_reel_input( Input.is_action_pressed("fish_primary") @@ -969,7 +1087,7 @@ func _on_catch_encounter_updated( barrier_health: PackedInt32Array, barrier_max_health: PackedInt32Array, active_barrier_index: int, - visible: bool, + encounter_visible: bool, ) -> void: catch_display_changed.emit( progress, @@ -978,9 +1096,9 @@ func _on_catch_encounter_updated( barrier_health, barrier_max_health, active_barrier_index, - visible + encounter_visible ) - if state != FishingState.FIGHTING or not visible: + if state != FishingState.FIGHTING or not encounter_visible: return @@ -993,10 +1111,12 @@ func _on_catch_completed() -> void: _cancel_attempt() return - _pending_catch = _fish_selector.create_catch(_selected_fish) if _pending_catch == null or not _pending_catch.is_valid(): _cancel_attempt() return + _stop_fight_audio() + _active_player.set_fighting_visual(false) + _active_player.set_retract_visual() state = FishingState.SHOWING_CATCH _showcase_ready = false _showcase_outcome_completed = false @@ -1010,13 +1130,17 @@ func _on_catch_escaped() -> void: if state != FishingState.FIGHTING: return - var fish_name: String = "the fish" - if _selected_fish != null and not _selected_fish.display_name.is_empty(): - fish_name = "the %s" % _selected_fish.display_name - _cleanup_attempt("%s got away!" % fish_name, &"escape") + _stop_fight_audio() + _cleanup_attempt("", &"escape") func _on_outcome_completed(outcome: StringName) -> void: + var retracting_player := _active_player + if ( + retracting_player != null + and not retracting_player.is_retract_visual_complete() + ): + await retracting_player.retract_visual_finished if state == FishingState.RETURNING: var cleanup_message: String = _pending_cleanup_message _pending_cleanup_message = "" @@ -1116,6 +1240,20 @@ func _cleanup_attempt( cooldown_message: String = "", visual_outcome: StringName = &"", ) -> void: + _set_bite_confirmation_pending(false) + _stop_fight_audio() + _stop_reeling_audio() + if _active_player != null: + _active_player.set_fighting_visual(false) + if visual_outcome.is_empty(): + _active_player.set_fishing_visual(false) + elif visual_outcome == &"catch": + # A successful catch already retracted the line before the fish + # showcase. Returning from the pocket transition should settle back + # to idle instead of replaying the retract animation. + _active_player.set_fishing_visual(false) + else: + _active_player.set_retract_visual() if not visual_outcome.is_empty(): if state == FishingState.RETURNING: return @@ -1144,6 +1282,8 @@ func _cleanup_attempt( func _finalize_attempt_cleanup(cooldown_message: String) -> void: _showcase_restore_generation += 1 if _active_player != null: + _active_player.set_fighting_visual(false) + _active_player.set_fishing_visual(false) _active_player.end_catch_showcase() _active_player.set_movement_enabled(true) _active_player = null @@ -1226,6 +1366,14 @@ func _capture_cast_direction(player: PlayerType) -> Vector3: return direction.normalized() +func _get_cast_launch_position() -> Vector3: + if _active_player != null: + var rod_tip: Marker3D = _active_player.get_fishing_rod_tip() + if rod_tip != null and is_instance_valid(rod_tip): + return rod_tip.global_position + return _cast_origin_position + + func _calculate_cast_target(distance: float) -> Vector3: var query_position := Vector3( _cast_origin_position.x + _cast_direction.x * distance, @@ -1271,6 +1419,7 @@ func resolve_fishing_surface( get_world_3d().direct_space_state, target, resolved_reference_y, + water_occlusion_tolerance, ) @@ -1281,17 +1430,25 @@ func is_target_fishable(target: Vector3) -> bool: func _is_cast_target_valid(target: Vector3) -> bool: return ( is_target_fishable(target) - and is_cast_path_clear(_cast_origin_position, target) + and is_cast_path_clear(_get_cast_launch_position(), target) ) func is_cast_path_clear(origin: Vector3, target: Vector3) -> bool: - return _surface_resolver.find_first_cast_collision( + if _preferred_cast_arc_height < 0.0: + _preferred_cast_arc_height = maxf( + _presentation.cast_arc_height, + 0.0, + ) + var resolved_arc: Dictionary = _surface_resolver.resolve_cast_arc( get_world_3d().direct_space_state, origin, target, - _presentation.cast_arc_height, - ).is_empty() + _preferred_cast_arc_height, + ) + _presentation.cast_arc_height = float(resolved_arc["arc_height"]) + var collision: Dictionary = resolved_arc["collision"] + return collision.is_empty() func _resolve_cast_impact_position(origin: Vector3, target: Vector3) -> Vector3: @@ -1332,7 +1489,7 @@ func _build_fishing_context( context.location_tags = region.location_tags.duplicate() context.water_type = region.water_type context.active_event_tags = context_event_tags.duplicate() - context.active_bait_tags = context_bait_tags.duplicate() + context.active_bait_tags = _get_active_bait_tags() if _world_time != null: context.is_night = _world_time.is_night_period() context.is_day_night_transition = _world_time.is_transition() @@ -1348,6 +1505,31 @@ func build_network_context( return _build_fishing_context(region) +func _get_active_bait_tags() -> Array[StringName]: + if _local_player == null or _item_catalog == null: + return [] + var bait_id: StringName = _local_player.active_bait_id + if bait_id.is_empty() or _local_bag == null or not _local_bag.owns_item(bait_id): + return [] + var item: ItemDataType = _item_catalog.get_item_by_id(bait_id) + return item.bait_tags.duplicate() if item != null and item.is_bait() else [] + + +func _active_lure_has_effect(effect_id: StringName) -> bool: + if ( + _local_player == null + or _item_catalog == null + or _local_bag == null + or _local_player.active_lure_id.is_empty() + or not _local_bag.owns_item(_local_player.active_lure_id) + ): + return false + var lure: ItemDataType = _item_catalog.get_item_by_id( + _local_player.active_lure_id + ) + return lure != null and lure.is_lure() and effect_id in lure.lure_effects + + func _build_network_evidence() -> Dictionary: var rarity_multipliers: Array[float] = [] for rarity: int in range(FishDataType.Rarity.size()): @@ -1358,6 +1540,8 @@ func _build_network_evidence() -> Dictionary: discovered_ids.append(str(fish_id)) var item: ItemDataType = _get_active_item() return { + "bait_id": str(_active_player.active_bait_id), + "lure_id": str(_active_player.active_lure_id), "rod_id": str(item.item_id) if item != null else "", "reel_speed": _active_player.reel_speed * ( _fishing_upgrades.get_reel_speed_multiplier() @@ -1378,6 +1562,13 @@ func _on_network_item_use_finished( accepted: bool, message: String, ) -> void: + if ( + not accepted + and message == PlayerItemEffectsType.FISH_FINDER_DEAD_MESSAGE + ): + status_changed.emit("") + local_speech_requested.emit(message) + return status_changed.emit(message) if accepted: refresh_active_item_status() @@ -1388,15 +1579,22 @@ func _on_network_cast_accepted( target: Vector3, ) -> void: if state != FishingState.CASTING: + # A cast response can arrive after water recovery has already cancelled + # its local presentation. Do not leave that late authoritative attempt + # alive with its movement lock still applied. + if _network_fishing != null and _network_fishing.has_local_attempt(): + _network_fishing.cancel_local_attempt("") return _cast_target = target _bobber_water_position = target state = FishingState.WAITING_FOR_BITE + if _active_player != null: + _active_player.set_fishing_visual(true) _network_primary_input_held = false _network_input_resend_elapsed = 0.0 _presentation.show_withdrawal_position(_bobber_water_position) _presentation.set_line_mode(FishingPresentationType.LineMode.SLACK) - status_changed.emit("waiting for a bite...") + status_changed.emit("") func _on_network_cast_rejected(message: String) -> void: @@ -1415,15 +1613,33 @@ func _on_network_cast_rejected(message: String) -> void: _cleanup_attempt(visible_message, &"invalid") +func _on_network_bite_pending(_attempt_id: String) -> void: + if state != FishingState.WAITING_FOR_BITE: + return + _stop_reeling_audio() + _withdrawal_input_held = false + _network_primary_input_held = false + _network_input_resend_elapsed = 0.0 + _set_bite_confirmation_pending(true) + status_changed.emit("") + _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) + _presentation.show_bite() + + func _on_network_bite_started(_attempt_id: String) -> void: if state != FishingState.WAITING_FOR_BITE: return + _stop_reeling_audio() + _set_bite_confirmation_pending(false) state = FishingState.FIGHTING + if _active_player != null: + _active_player.set_fighting_visual(true) _withdrawal_input_held = false _network_primary_input_held = Input.is_action_pressed("fish_primary") _network_input_resend_elapsed = 0.0 _network_auto_click_accumulator = 0.0 _network_active_barrier_index = -1 + _start_fight_audio() bite_activated.emit() status_changed.emit("fish on!") _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) @@ -1445,7 +1661,10 @@ func _resend_network_input_state(delta: float) -> void: _network_input_resend_elapsed, NETWORK_INPUT_RESEND_INTERVAL_SECONDS, ) - _network_fishing.submit_local_input(_network_primary_input_held, false) + _network_fishing.submit_local_input( + false if _bite_confirmation_pending else _network_primary_input_held, + _bite_confirmation_pending and _bite_confirmation_requested, + ) func _on_network_fishing_snapshot(snapshot: Dictionary) -> void: @@ -1506,7 +1725,11 @@ func _update_network_auto_click(delta: float) -> void: func _on_network_catch_received(fish_catch: FishCatchType) -> void: if state != FishingState.FIGHTING or fish_catch == null: return + _stop_fight_audio() _pending_catch = fish_catch + if _active_player != null: + _active_player.set_fighting_visual(false) + _active_player.set_fishing_visual(false) state = FishingState.SHOWING_CATCH _showcase_ready = false _showcase_outcome_completed = false @@ -1516,6 +1739,8 @@ func _on_network_catch_received(fish_catch: FishCatchType) -> void: PackedInt32Array(), -1, false ) _presentation.set_line_mode(FishingPresentationType.LineMode.TAUT) + if _active_player != null: + _active_player.set_retract_visual() _presentation.play_outcome(&"catch") @@ -1537,6 +1762,57 @@ func _on_network_attempt_ended( _cleanup_attempt(message, visual_outcome) +func _on_network_session_state_changed(state_value: NetworkSession.State) -> void: + if state_value in [ + NetworkSession.State.INACTIVE, + NetworkSession.State.DISCONNECTING, + NetworkSession.State.CONNECTION_FAILED, + NetworkSession.State.SERVER_LOST, + ]: + _stop_fight_audio() + _stop_reeling_audio() + + +func _start_fight_audio() -> void: + if _fight_audio == null or _fight_audio.stream == null: + return + if not _fight_audio.playing: + _fight_audio.play() + + +func _play_bobber_audio() -> void: + if _bobber_audio == null or _bobber_audio.stream == null: + return + _bobber_audio.play() + + +func _stop_fight_audio() -> void: + if _fight_audio != null and _fight_audio.playing: + _fight_audio.stop() + + +func _configure_audio_loop(stream: AudioStreamWAV) -> void: + stream.loop_mode = AudioStreamWAV.LOOP_FORWARD + if stream.loop_end <= stream.loop_begin: + stream.loop_begin = 0 + stream.loop_end = maxi( + 1, + roundi(stream.get_length() * float(stream.mix_rate)), + ) + + +func _start_reeling_audio() -> void: + if _reeling_audio == null or _reeling_audio.stream == null: + return + if not _reeling_audio.playing: + _reeling_audio.play() + + +func _stop_reeling_audio() -> void: + if _reeling_audio != null and _reeling_audio.playing: + _reeling_audio.stop() + + func resolve_safe_withdrawal_surface( from: Vector3, to: Vector3, diff --git a/fishing/fishing_spot.tscn b/fishing/fishing_spot.tscn index 322f618..ef6d0bf 100644 --- a/fishing/fishing_spot.tscn +++ b/fishing/fishing_spot.tscn @@ -1,6 +1,9 @@ -[gd_scene load_steps=12 format=3] +[gd_scene load_steps=15 format=3] [ext_resource type="Script" path="res://fishing/fishing_spot.gd" id="1_spot"] +[ext_resource type="AudioStream" path="res://audio/sfx/fishing/fighting.wav" id="2_fighting_audio"] +[ext_resource type="AudioStream" path="res://audio/sfx/fishing/reeling.wav" id="2_reeling_audio"] +[ext_resource type="AudioStream" path="res://audio/sfx/fishing/bobber.wav" id="2_bobber_audio"] [ext_resource type="Script" path="res://fishing/fishing_presentation.gd" id="3_presentation"] [ext_resource type="Script" path="res://fishing/catch_controller.gd" id="4_catch"] @@ -14,6 +17,7 @@ roughness = 0.45 [sub_resource type="StandardMaterial3D" id="LineMaterial"] shading_mode = 0 +cull_mode = 2 albedo_color = Color(0.9, 0.9, 0.82, 1) [sub_resource type="CylinderMesh" id="CastTargetMesh"] @@ -41,6 +45,24 @@ size = Vector3(0.62, 0.04, 0.09) [node name="FishingSpot" type="Node3D"] script = ExtResource("1_spot") +[node name="BobberAudio" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("2_bobber_audio") +volume_db = -14.0 +bus = &"SFX" + +[node name="FightAudio" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("2_fighting_audio") +volume_db = -10.0 +bus = &"SFX" + +[node name="ReelingAudio" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("2_reeling_audio") +volume_db = -10.0 +bus = &"SFX" + [node name="CatchController" type="Node" parent="."] unique_name_in_owner = true script = ExtResource("4_catch") diff --git a/fishing/fishing_surface_resolver.gd b/fishing/fishing_surface_resolver.gd index 08818ce..821d8cc 100644 --- a/fishing/fishing_surface_resolver.gd +++ b/fishing/fishing_surface_resolver.gd @@ -23,6 +23,7 @@ func resolve_surface( space_state: PhysicsDirectSpaceState3D, query_position: Vector3, reference_y: float, + maximum_solid_rise: float = INF, ) -> FishingSurfaceSampleType: var sample := FishingSurfaceSampleType.new() var ray_top: float = maxf(query_position.y, reference_y) + ray_start_height @@ -33,12 +34,18 @@ func resolve_surface( ray_top, ray_bottom, ) - var solid_hit: Dictionary = _find_top_solid_surface( - space_state, - query_position, + var solid_ray_top: float = minf( ray_top, - ray_bottom, + reference_y + maximum_solid_rise, ) + var solid_hit: Dictionary = {} + if solid_ray_top > ray_bottom: + solid_hit = _find_top_solid_surface( + space_state, + query_position, + solid_ray_top, + ray_bottom, + ) if water_region != null: var water_height: float = water_region.get_surface_height() @@ -101,6 +108,40 @@ func find_first_cast_collision( return {} +func resolve_cast_arc( + space_state: PhysicsDirectSpaceState3D, + origin: Vector3, + target: Vector3, + preferred_arc_height: float, +) -> Dictionary: + var raised_arc_height: float = maxf(preferred_arc_height, 0.0) + var raised_collision := find_first_cast_collision( + space_state, + origin, + target, + raised_arc_height, + ) + if raised_collision.is_empty() or is_zero_approx(raised_arc_height): + return { + "arc_height": raised_arc_height, + "collision": raised_collision, + } + + # A canopy or pier roof can intersect the decorative raised arc even when + # the direct path out toward the water is unobstructed. Prefer that low cast + # instead of visibly sending the bobber and line upward into the ceiling. + var direct_collision := find_first_cast_collision( + space_state, + origin, + target, + 0.0, + ) + return { + "arc_height": 0.0, + "collision": direct_collision, + } + + func resolve_withdrawal_surface( space_state: PhysicsDirectSpaceState3D, current_position: Vector3, diff --git a/fishing/fishing_surface_sample.gd b/fishing/fishing_surface_sample.gd index 4d2dd2c..121658b 100644 --- a/fishing/fishing_surface_sample.gd +++ b/fishing/fishing_surface_sample.gd @@ -4,6 +4,9 @@ extends RefCounted const FishableWaterRegionType = preload( "res://world/fishable_water_region.gd" ) +const WaterSurfaceMotionType = preload( + "res://world/water_surface_motion.gd" +) var has_surface: bool = false var position: Vector3 = Vector3.ZERO @@ -19,7 +22,12 @@ func is_fishable() -> bool: func get_marker_position(vertical_offset: float) -> Vector3: if not has_surface: return position - return position + normal.normalized() * vertical_offset + var presentation_offset := vertical_offset + if is_water_surface: + presentation_offset += ( + WaterSurfaceMotionType.get_default_height_offset() + ) + return position + normal.normalized() * presentation_offset func get_bobber_position(solid_clearance: float) -> Vector3: diff --git a/fishing/remote_fishing_presentation.gd b/fishing/remote_fishing_presentation.gd index 6399819..5a5b05b 100644 --- a/fishing/remote_fishing_presentation.gd +++ b/fishing/remote_fishing_presentation.gd @@ -1,6 +1,11 @@ class_name RemoteFishingPresentation extends Node3D +const WaterSurfaceMotionType = preload( + "res://world/water_surface_motion.gd" +) +const LINE_THICKNESS: float = 0.016 + signal return_completed var _owner: Player @@ -15,6 +20,7 @@ var _return_tween: Tween var _showcase_tween: Tween var _return_showcase_catch: FishCatch var _bobber_idle_elapsed: float = 0.0 +var _bobber_base_scale: Vector3 = Vector3.ONE func setup(owning_player: Player) -> void: @@ -33,6 +39,7 @@ func setup(owning_player: Player) -> void: _line.mesh = _line_mesh var line_material := StandardMaterial3D.new() line_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + line_material.cull_mode = BaseMaterial3D.CULL_DISABLED line_material.albedo_color = Color(0.9, 0.9, 0.82, 1.0) _line.material_override = line_material add_child(_line) @@ -48,14 +55,16 @@ func show_cast(origin: Vector3, target: Vector3) -> void: return _kill_cast_tween() _active = true + _bobber_base_scale = Vector3.ONE * _owner.get_character_visual_scale() _target = origin _pending_target = target _bobber_idle_elapsed = 0.0 _bobber.global_position = origin - _bobber.scale = Vector3.ONE + _bobber.scale = _bobber_base_scale _bobber.visible = true _line.visible = true _owner.set_active_item_is_rod(true) + _owner.set_release_visual() _redraw_line() _cast_tween = create_tween() _cast_tween.set_trans(Tween.TRANS_SINE) @@ -83,12 +92,17 @@ func update_bobber(world_position: Vector3) -> void: func show_bite() -> void: if not _active: return + if _owner != null and is_instance_valid(_owner): + _owner.set_fighting_visual(true) var tween: Tween = create_tween() - tween.tween_property(_bobber, "scale", Vector3.ONE * 0.7, 0.08) - tween.tween_property(_bobber, "scale", Vector3.ONE, 0.12) + tween.tween_property(_bobber, "scale", _bobber_base_scale * 0.7, 0.08) + tween.tween_property(_bobber, "scale", _bobber_base_scale, 0.12) func play_return(showcase_catch: FishCatch = null) -> void: + if _owner != null and is_instance_valid(_owner): + _owner.set_fighting_visual(false) + _owner.set_retract_visual() if not _active or _bobber == null: cleanup() return_completed.emit() @@ -124,10 +138,13 @@ func cleanup() -> void: _return_showcase_catch = null _bobber_idle_elapsed = 0.0 if _owner != null and is_instance_valid(_owner): + _owner.set_fighting_visual(false) + _owner.set_fishing_visual(false) _owner.end_catch_showcase(Callable(), true) if _bobber != null: _bobber.visible = false _bobber.scale = Vector3.ONE + _bobber_base_scale = Vector3.ONE if _line != null: _line.visible = false _line_mesh.clear_surfaces() @@ -140,19 +157,28 @@ func _set_cast_sample( ) -> void: _target = start.lerp(target, progress) _target.y += sin(progress * PI) * 2.0 + _target.y += ( + WaterSurfaceMotionType.get_default_height_offset() + * smoothstep(0.72, 1.0, progress) + ) _bobber.global_position = _target func _on_cast_finished() -> void: _cast_tween = null _bobber_idle_elapsed = 0.0 + if _owner != null and is_instance_valid(_owner): + _owner.set_fishing_visual(true) _apply_bobber_idle_motion() _redraw_line() func _apply_bobber_idle_motion() -> void: var phase: float = _bobber_idle_elapsed * 0.7 * TAU - _target = _pending_target + Vector3.UP * sin(phase) * 0.035 + _target = _pending_target + Vector3.UP * ( + WaterSurfaceMotionType.get_default_height_offset() + + sin(phase) * 0.035 + ) _bobber.global_position = _target _bobber.rotation.z = deg_to_rad(4.0) * sin(phase * 0.73) @@ -175,6 +201,12 @@ func _on_return_finished() -> void: _bobber.visible = false _line.visible = false _line_mesh.clear_surfaces() + if ( + _owner != null + and is_instance_valid(_owner) + and not _owner.is_retract_visual_complete() + ): + await _owner.retract_visual_finished if ( _return_showcase_catch != null and _owner != null @@ -192,7 +224,12 @@ func _on_return_finished() -> void: func _on_showcase_finished() -> void: _showcase_tween = null if _owner != null and is_instance_valid(_owner): - _owner.end_catch_showcase() + _owner.end_catch_showcase(_on_showcase_put_away_finished) + return + _on_showcase_put_away_finished() + + +func _on_showcase_put_away_finished() -> void: return_completed.emit() @@ -234,10 +271,50 @@ func _redraw_line() -> void: if tip == null: return _line_mesh.clear_surfaces() - _line_mesh.surface_begin(Mesh.PRIMITIVE_LINE_STRIP) - _line_mesh.surface_add_vertex(to_local(tip.global_position)) var midpoint: Vector3 = tip.global_position.lerp(_target, 0.5) midpoint.y -= 0.12 - _line_mesh.surface_add_vertex(to_local(midpoint)) - _line_mesh.surface_add_vertex(to_local(_target)) + _draw_line_ribbon(PackedVector3Array([ + tip.global_position, + midpoint, + _target, + ])) + + +func _draw_line_ribbon(points: PackedVector3Array) -> void: + var camera: Camera3D = get_viewport().get_camera_3d() + if camera == null: + _line_mesh.surface_begin(Mesh.PRIMITIVE_LINE_STRIP) + for point: Vector3 in points: + _line_mesh.surface_add_vertex(to_local(point)) + _line_mesh.surface_end() + return + + var half_thickness: float = LINE_THICKNESS * 0.5 + _line_mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) + for point_index: int in range(1, points.size()): + var start: Vector3 = points[point_index - 1] + var end: Vector3 = points[point_index] + var segment: Vector3 = end - start + if segment.length_squared() <= 0.000001: + continue + var to_camera: Vector3 = ( + camera.global_position - start.lerp(end, 0.5) + ) + var side: Vector3 = segment.normalized().cross(to_camera.normalized()) + if side.length_squared() <= 0.000001: + side = segment.normalized().cross(Vector3.UP) + if side.length_squared() <= 0.000001: + side = segment.normalized().cross(Vector3.RIGHT) + side = side.normalized() * half_thickness + + var start_left: Vector3 = to_local(start - side) + var start_right: Vector3 = to_local(start + side) + var end_right: Vector3 = to_local(end + side) + var end_left: Vector3 = to_local(end - side) + _line_mesh.surface_add_vertex(start_left) + _line_mesh.surface_add_vertex(start_right) + _line_mesh.surface_add_vertex(end_right) + _line_mesh.surface_add_vertex(start_left) + _line_mesh.surface_add_vertex(end_right) + _line_mesh.surface_add_vertex(end_left) _line_mesh.surface_end() diff --git a/icon.svg b/icon.svg deleted file mode 100644 index c6bbb7d..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 1b9da13..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cxpiqjwgff0xr" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/inventory/player_bag.gd b/inventory/player_bag.gd index 1e885ee..4fc12b6 100644 --- a/inventory/player_bag.gd +++ b/inventory/player_bag.gd @@ -5,10 +5,15 @@ const ItemCatalogType = preload("res://items/item_catalog.gd") const ItemDataType = preload("res://items/item_data.gd") const OwnedItemType = preload("res://items/owned_item.gd") +const DEFAULT_UNLOCKED_BAIT_IDS: Array[StringName] = [&"worms"] + signal contents_changed var _catalog: ItemCatalogType var _items: Array[OwnedItemType] = [] +var _unlocked_bait_ids: Array[StringName] = ( + DEFAULT_UNLOCKED_BAIT_IDS.duplicate() +) func setup(catalog: ItemCatalogType) -> void: @@ -27,6 +32,8 @@ func add_item(item_id: StringName, quantity: int = 1) -> bool: owned.item_id = item_id owned.quantity = quantity _items.append(owned) + if item.is_bait() and not _unlocked_bait_ids.has(item_id): + _unlocked_bait_ids.append(item_id) contents_changed.emit() return true @@ -85,6 +92,46 @@ func get_all_items() -> Array[OwnedItemType]: return result +func get_unlocked_bait_ids() -> Array[StringName]: + return _unlocked_bait_ids.duplicate() + + +func is_bait_unlocked(item_id: StringName) -> bool: + return _unlocked_bait_ids.has(item_id) + + +func get_unlocked_bait_items() -> Array[OwnedItemType]: + var result: Array[OwnedItemType] = [] + for item_id: StringName in _unlocked_bait_ids: + var owned: OwnedItemType = get_owned_item(item_id) + if owned != null: + result.append(owned.duplicate_record()) + continue + var empty_record := OwnedItemType.new() + empty_record.item_id = item_id + empty_record.quantity = 0 + result.append(empty_record) + return result + + +func replace_unlocked_bait_ids(item_ids: Array[StringName]) -> bool: + var validated: Array[StringName] = DEFAULT_UNLOCKED_BAIT_IDS.duplicate() + var seen: Dictionary[StringName, bool] = {} + for item_id: StringName in item_ids: + if item_id.is_empty() or seen.has(item_id): + return false + seen[item_id] = true + if validated.has(item_id): + continue + var item: ItemDataType = _resolve_valid_item(item_id) + if item == null or not item.is_bait(): + return false + validated.append(item_id) + _unlocked_bait_ids = validated + contents_changed.emit() + return true + + func replace_all_items(items: Array[OwnedItemType]) -> bool: var validated: Array[OwnedItemType] = [] var seen: Dictionary[StringName, bool] = {} diff --git a/items/catalog/art_kit.tres b/items/catalog/art_kit.tres index 3bbc0f6..ee5b28e 100644 --- a/items/catalog/art_kit.tres +++ b/items/catalog/art_kit.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] [ext_resource type="Script" path="res://items/item_data.gd" id="1_item"] -[ext_resource type="Texture2D" path="res://items/icons/placeholder/art_kit.svg" id="2_icon"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit.png" id="2_icon"] [resource] script = ExtResource("1_item") diff --git a/items/catalog/batteries.tres b/items/catalog/batteries.tres new file mode 100644 index 0000000..9f26650 --- /dev/null +++ b/items/catalog/batteries.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"] + +[resource] +script = ExtResource("1_item") +item_id = &"batteries" +display_name = "batteries" +description = "replacement batteries for the fish finder." +category = 3 +stackable = true +max_stack = 99 +usable = false +equippable = false +hotbar_allowed = false diff --git a/items/catalog/fish_finder.tres b/items/catalog/fish_finder.tres index 4c6d4a2..a3fb6c4 100644 --- a/items/catalog/fish_finder.tres +++ b/items/catalog/fish_finder.tres @@ -7,10 +7,11 @@ script = ExtResource("1") item_id = &"fish_finder" display_name = "fish finder" -description = "temporarily shortens bite waits and improves rarity weights." -category = 3 +description = "uses one battery to temporarily shorten bite waits and improve rarity weights." +category = 1 icon = ExtResource("2") -stackable = true -max_stack = 99 +stackable = false +max_stack = 1 usable = true +equippable = true hotbar_allowed = true diff --git a/items/catalog/item_catalog.tres b/items/catalog/item_catalog.tres index 20f4bd1..43377ba 100644 --- a/items/catalog/item_catalog.tres +++ b/items/catalog/item_catalog.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="ItemCatalog" load_steps=9 format=3] +[gd_resource type="Resource" script_class="ItemCatalog" load_steps=18 format=3] [ext_resource type="Script" path="res://items/item_catalog.gd" id="1_script"] [ext_resource type="Resource" path="res://items/catalog/basic_fishing_rod.tres" id="2_rod"] @@ -8,7 +8,16 @@ [ext_resource type="Resource" path="res://items/catalog/fish_finder.tres" id="6_finder"] [ext_resource type="Resource" path="res://items/catalog/cooler_expansion.tres" id="7_cooler"] [ext_resource type="Resource" path="res://items/catalog/art_kit.tres" id="8_art_kit"] +[ext_resource type="Resource" path="res://items/catalog/worms.tres" id="9_worms"] +[ext_resource type="Resource" path="res://items/catalog/snails.tres" id="10_snails"] +[ext_resource type="Resource" path="res://items/catalog/shrimp.tres" id="11_shrimp"] +[ext_resource type="Resource" path="res://items/catalog/squid_chunks.tres" id="12_squid"] +[ext_resource type="Resource" path="res://items/catalog/whole_anchovy.tres" id="13_anchovy"] +[ext_resource type="Resource" path="res://items/catalog/whole_sardine.tres" id="14_sardine"] +[ext_resource type="Resource" path="res://items/catalog/luminous_roe.tres" id="15_roe"] +[ext_resource type="Resource" path="res://items/catalog/the_standby.tres" id="16_standby"] +[ext_resource type="Resource" path="res://items/catalog/batteries.tres" id="17_batteries"] [resource] script = ExtResource("1_script") -items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit")] +items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit"), ExtResource("9_worms"), ExtResource("10_snails"), ExtResource("11_shrimp"), ExtResource("12_squid"), ExtResource("14_sardine"), ExtResource("13_anchovy"), ExtResource("15_roe"), ExtResource("16_standby"), ExtResource("17_batteries")] diff --git a/items/catalog/luminous_roe.tres b/items/catalog/luminous_roe.tres new file mode 100644 index 0000000..7814f42 --- /dev/null +++ b/items/catalog/luminous_roe.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_roe_luminous.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"luminous_roe" +display_name = "luminous roe" +description = "rare glowing roe with the strongest rare catch odds." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_6"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/shrimp.tres b/items/catalog/shrimp.tres new file mode 100644 index 0000000..e308163 --- /dev/null +++ b/items/catalog/shrimp.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_shrimp.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"shrimp" +display_name = "shrimp" +description = "lively bait that noticeably improves rare catch chances." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_2"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/snails.tres b/items/catalog/snails.tres new file mode 100644 index 0000000..12d4497 --- /dev/null +++ b/items/catalog/snails.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_snail.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"snails" +display_name = "snails" +description = "slow-moving bait with a better chance for rarer catches." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_1"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/squid_chunks.tres b/items/catalog/squid_chunks.tres new file mode 100644 index 0000000..dfe245f --- /dev/null +++ b/items/catalog/squid_chunks.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_squid.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"squid_chunks" +display_name = "squid chunks" +description = "rich cut bait that strongly favors rarer catches." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_3"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/the_standby.tres b/items/catalog/the_standby.tres new file mode 100644 index 0000000..1fcc92e --- /dev/null +++ b/items/catalog/the_standby.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"] + +[resource] +script = ExtResource("1_item") +item_id = &"the_standby" +display_name = "The Standby" +description = "holds a bite until you're ready to start the fight." +category = 7 +lure_effects = Array[StringName]([&"deferred_fight"]) +stackable = false +max_stack = 1 +usable = false +equippable = true +hotbar_allowed = false diff --git a/items/catalog/whole_anchovy.tres b/items/catalog/whole_anchovy.tres new file mode 100644 index 0000000..cbc60b6 --- /dev/null +++ b/items/catalog/whole_anchovy.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_sardine.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"whole_anchovy" +display_name = "sardine" +description = "valuable oily schooling bait tuned for exceptional catches." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_5"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/whole_sardine.tres b/items/catalog/whole_sardine.tres new file mode 100644 index 0000000..c5d0163 --- /dev/null +++ b/items/catalog/whole_sardine.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_minnow.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"whole_sardine" +display_name = "minnow" +description = "small live bait with high rare catch potential." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"bait_rarity_4"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/catalog/worms.tres b/items/catalog/worms.tres new file mode 100644 index 0000000..f01258e --- /dev/null +++ b/items/catalog/worms.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3] + +[ext_resource type="Script" path="res://items/item_data.gd" id="1"] +[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_worms.png" id="2_icon"] + +[resource] +script = ExtResource("1") +item_id = &"worms" +display_name = "worms" +description = "basic bait that opens a small chance for rarer catches." +icon = ExtResource("2_icon") +category = 2 +bait_tags = Array[StringName]([&"worm", &"bait_rarity_0"]) +stackable = true +max_stack = 10 +hotbar_allowed = false diff --git a/items/icons/art/art_kit.png b/items/icons/art/art_kit.png new file mode 100644 index 0000000..39bc32b Binary files /dev/null and b/items/icons/art/art_kit.png differ diff --git a/art/exported/environment/textures/grass.png.import b/items/icons/art/art_kit.png.import similarity index 73% rename from art/exported/environment/textures/grass.png.import rename to items/icons/art/art_kit.png.import index 9d7961f..053fd1f 100644 --- a/art/exported/environment/textures/grass.png.import +++ b/items/icons/art/art_kit.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4n8bf4cgua4s" -path="res://.godot/imported/grass.png-4d1108b11a421d9c16bd72fb0bc993d3.ctex" +uid="uid://datu7j7tq2k3e" +path="res://.godot/imported/art_kit.png-b54a66a446203c844f97c37241065423.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://art/exported/environment/textures/grass.png" -dest_files=["res://.godot/imported/grass.png-4d1108b11a421d9c16bd72fb0bc993d3.ctex"] +source_file="res://items/icons/art/art_kit.png" +dest_files=["res://.godot/imported/art_kit.png-b54a66a446203c844f97c37241065423.ctex"] [params] diff --git a/items/icons/art/art_kit_eraser.png b/items/icons/art/art_kit_eraser.png new file mode 100644 index 0000000..6eb3003 Binary files /dev/null and b/items/icons/art/art_kit_eraser.png differ diff --git a/items/icons/art/art_kit_eraser.png.import b/items/icons/art/art_kit_eraser.png.import new file mode 100644 index 0000000..a25a8d1 --- /dev/null +++ b/items/icons/art/art_kit_eraser.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdbba3bvhuear" +path="res://.godot/imported/art_kit_eraser.png-4df8445ba3139b8236508fc61a024158.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_eraser.png" +dest_files=["res://.godot/imported/art_kit_eraser.png-4df8445ba3139b8236508fc61a024158.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_finish_light.png b/items/icons/art/art_kit_grid_finish_light.png new file mode 100644 index 0000000..684efed Binary files /dev/null and b/items/icons/art/art_kit_grid_finish_light.png differ diff --git a/items/icons/art/art_kit_grid_finish_light.png.import b/items/icons/art/art_kit_grid_finish_light.png.import new file mode 100644 index 0000000..63d8261 --- /dev/null +++ b/items/icons/art/art_kit_grid_finish_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dm24dy3ebvqmv" +path="res://.godot/imported/art_kit_grid_finish_light.png-fe16d574640fb174cb8b6ff3cfb95e5e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_finish_light.png" +dest_files=["res://.godot/imported/art_kit_grid_finish_light.png-fe16d574640fb174cb8b6ff3cfb95e5e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_hidden_light.png b/items/icons/art/art_kit_grid_hidden_light.png new file mode 100644 index 0000000..af01077 Binary files /dev/null and b/items/icons/art/art_kit_grid_hidden_light.png differ diff --git a/items/icons/art/art_kit_grid_hidden_light.png.import b/items/icons/art/art_kit_grid_hidden_light.png.import new file mode 100644 index 0000000..95d7ac3 --- /dev/null +++ b/items/icons/art/art_kit_grid_hidden_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv6yicrysb0vs" +path="res://.godot/imported/art_kit_grid_hidden_light.png-e92350d8a3e2c3e3bf286658f1ae0c41.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_hidden_light.png" +dest_files=["res://.godot/imported/art_kit_grid_hidden_light.png-e92350d8a3e2c3e3bf286658f1ae0c41.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_light.png b/items/icons/art/art_kit_grid_light.png new file mode 100644 index 0000000..58089a3 Binary files /dev/null and b/items/icons/art/art_kit_grid_light.png differ diff --git a/items/icons/art/art_kit_grid_light.png.import b/items/icons/art/art_kit_grid_light.png.import new file mode 100644 index 0000000..6435819 --- /dev/null +++ b/items/icons/art/art_kit_grid_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dh2tu3ptqe6f7" +path="res://.godot/imported/art_kit_grid_light.png-d445eba68746a34d73bbf1744fe56777.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_light.png" +dest_files=["res://.godot/imported/art_kit_grid_light.png-d445eba68746a34d73bbf1744fe56777.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_restore_light.png b/items/icons/art/art_kit_grid_restore_light.png new file mode 100644 index 0000000..9517e6d Binary files /dev/null and b/items/icons/art/art_kit_grid_restore_light.png differ diff --git a/items/icons/art/art_kit_grid_restore_light.png.import b/items/icons/art/art_kit_grid_restore_light.png.import new file mode 100644 index 0000000..9672eab --- /dev/null +++ b/items/icons/art/art_kit_grid_restore_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ckxdpi5sqerao" +path="res://.godot/imported/art_kit_grid_restore_light.png-d2f2458a90dabc79430d7ff0e0e3385d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_restore_light.png" +dest_files=["res://.godot/imported/art_kit_grid_restore_light.png-d2f2458a90dabc79430d7ff0e0e3385d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_marker.png b/items/icons/art/art_kit_marker.png new file mode 100644 index 0000000..403f69c Binary files /dev/null and b/items/icons/art/art_kit_marker.png differ diff --git a/items/icons/art/art_kit_marker.png.import b/items/icons/art/art_kit_marker.png.import new file mode 100644 index 0000000..cffece6 --- /dev/null +++ b/items/icons/art/art_kit_marker.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b20kab6kxh7ap" +path="res://.godot/imported/art_kit_marker.png-77e7b82481305350436ed5cca046df56.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_marker.png" +dest_files=["res://.godot/imported/art_kit_marker.png-77e7b82481305350436ed5cca046df56.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_minnow.png b/items/icons/bait/64_bait_minnow.png new file mode 100644 index 0000000..f083d65 Binary files /dev/null and b/items/icons/bait/64_bait_minnow.png differ diff --git a/items/icons/bait/64_bait_minnow.png.import b/items/icons/bait/64_bait_minnow.png.import new file mode 100644 index 0000000..67f57f5 --- /dev/null +++ b/items/icons/bait/64_bait_minnow.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://oc1nckfxeir0" +path="res://.godot/imported/64_bait_minnow.png-25bb645caffd0cad87b30d6b38b80ca4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_minnow.png" +dest_files=["res://.godot/imported/64_bait_minnow.png-25bb645caffd0cad87b30d6b38b80ca4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_roe_luminous.png b/items/icons/bait/64_bait_roe_luminous.png new file mode 100644 index 0000000..dc89fbd Binary files /dev/null and b/items/icons/bait/64_bait_roe_luminous.png differ diff --git a/items/icons/bait/64_bait_roe_luminous.png.import b/items/icons/bait/64_bait_roe_luminous.png.import new file mode 100644 index 0000000..b0a78d0 --- /dev/null +++ b/items/icons/bait/64_bait_roe_luminous.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnqji6j6nigjq" +path="res://.godot/imported/64_bait_roe_luminous.png-cfa7eaf935630a20e2e8111f803c7741.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_roe_luminous.png" +dest_files=["res://.godot/imported/64_bait_roe_luminous.png-cfa7eaf935630a20e2e8111f803c7741.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_sardine.png b/items/icons/bait/64_bait_sardine.png new file mode 100644 index 0000000..9161ba5 Binary files /dev/null and b/items/icons/bait/64_bait_sardine.png differ diff --git a/items/icons/bait/64_bait_sardine.png.import b/items/icons/bait/64_bait_sardine.png.import new file mode 100644 index 0000000..03f94bf --- /dev/null +++ b/items/icons/bait/64_bait_sardine.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddbyrbcbm40i4" +path="res://.godot/imported/64_bait_sardine.png-28b5d49b506c3007ebf15dcc8f198a94.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_sardine.png" +dest_files=["res://.godot/imported/64_bait_sardine.png-28b5d49b506c3007ebf15dcc8f198a94.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_shrimp.png b/items/icons/bait/64_bait_shrimp.png new file mode 100644 index 0000000..8a20783 Binary files /dev/null and b/items/icons/bait/64_bait_shrimp.png differ diff --git a/items/icons/bait/64_bait_shrimp.png.import b/items/icons/bait/64_bait_shrimp.png.import new file mode 100644 index 0000000..8c65f4a --- /dev/null +++ b/items/icons/bait/64_bait_shrimp.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpq47xg8tv6th" +path="res://.godot/imported/64_bait_shrimp.png-f68293fc8a81e803d5eeb1f268e4f3d3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_shrimp.png" +dest_files=["res://.godot/imported/64_bait_shrimp.png-f68293fc8a81e803d5eeb1f268e4f3d3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_snail.png b/items/icons/bait/64_bait_snail.png new file mode 100644 index 0000000..39e6c16 Binary files /dev/null and b/items/icons/bait/64_bait_snail.png differ diff --git a/items/icons/bait/64_bait_snail.png.import b/items/icons/bait/64_bait_snail.png.import new file mode 100644 index 0000000..de8d419 --- /dev/null +++ b/items/icons/bait/64_bait_snail.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dihevd23dh5x3" +path="res://.godot/imported/64_bait_snail.png-9a7a0c9f93be442a8a1d685a5324e3f6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_snail.png" +dest_files=["res://.godot/imported/64_bait_snail.png-9a7a0c9f93be442a8a1d685a5324e3f6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_squid.png b/items/icons/bait/64_bait_squid.png new file mode 100644 index 0000000..57e2b64 Binary files /dev/null and b/items/icons/bait/64_bait_squid.png differ diff --git a/items/icons/bait/64_bait_squid.png.import b/items/icons/bait/64_bait_squid.png.import new file mode 100644 index 0000000..41bf2b4 --- /dev/null +++ b/items/icons/bait/64_bait_squid.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kf3aglrd1en3" +path="res://.godot/imported/64_bait_squid.png-6ec6c9776ffcae93fd2fa312c8a9013d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_squid.png" +dest_files=["res://.godot/imported/64_bait_squid.png-6ec6c9776ffcae93fd2fa312c8a9013d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/bait/64_bait_worms.png b/items/icons/bait/64_bait_worms.png new file mode 100644 index 0000000..51cb396 Binary files /dev/null and b/items/icons/bait/64_bait_worms.png differ diff --git a/items/icons/bait/64_bait_worms.png.import b/items/icons/bait/64_bait_worms.png.import new file mode 100644 index 0000000..5fbd6cd --- /dev/null +++ b/items/icons/bait/64_bait_worms.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bn6815x0rrspw" +path="res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/bait/64_bait_worms.png" +dest_files=["res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/consumables/64_consumable_coffee.png b/items/icons/consumables/64_consumable_coffee.png index 270f191..3282a17 100644 Binary files a/items/icons/consumables/64_consumable_coffee.png and b/items/icons/consumables/64_consumable_coffee.png differ diff --git a/items/icons/consumables/64_consumable_energybar.png b/items/icons/consumables/64_consumable_energybar.png index 370cf40..1657a6f 100644 Binary files a/items/icons/consumables/64_consumable_energybar.png and b/items/icons/consumables/64_consumable_energybar.png differ diff --git a/items/icons/consumables/64_consumable_energydrink_redfish.png b/items/icons/consumables/64_consumable_energydrink_redfish.png index e0f1457..92a55dc 100644 Binary files a/items/icons/consumables/64_consumable_energydrink_redfish.png and b/items/icons/consumables/64_consumable_energydrink_redfish.png differ diff --git a/items/icons/consumables/64_consumable_fishfinder.png b/items/icons/consumables/64_consumable_fishfinder.png index 2cace25..5c92ce1 100644 Binary files a/items/icons/consumables/64_consumable_fishfinder.png and b/items/icons/consumables/64_consumable_fishfinder.png differ diff --git a/items/icons/equipment/64_cooler_plus.png b/items/icons/equipment/64_cooler_plus.png new file mode 100644 index 0000000..acbd889 Binary files /dev/null and b/items/icons/equipment/64_cooler_plus.png differ diff --git a/items/icons/equipment/64_cooler_plus.png.import b/items/icons/equipment/64_cooler_plus.png.import new file mode 100644 index 0000000..44e493a --- /dev/null +++ b/items/icons/equipment/64_cooler_plus.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://baht2hredfky8" +path="res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/equipment/64_cooler_plus.png" +dest_files=["res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/placeholder/art_kit.svg b/items/icons/placeholder/art_kit.svg deleted file mode 100644 index bd89210..0000000 --- a/items/icons/placeholder/art_kit.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/items/icons/placeholder/art_kit.svg.import b/items/icons/placeholder/art_kit.svg.import deleted file mode 100644 index d28daa6..0000000 --- a/items/icons/placeholder/art_kit.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bm0kkevv62mu0" -path="res://.godot/imported/art_kit.svg-d4bd09e1da2e2339f7d505ee23d771f8.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://items/icons/placeholder/art_kit.svg" -dest_files=["res://.godot/imported/art_kit.svg-d4bd09e1da2e2339f7d505ee23d771f8.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/items/icons/placeholder/fish_coin.svg b/items/icons/placeholder/fish_coin.svg deleted file mode 100644 index ac4da92..0000000 --- a/items/icons/placeholder/fish_coin.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/items/icons/placeholder/fish_coin.svg.import b/items/icons/placeholder/fish_coin.svg.import deleted file mode 100644 index 1dca3c6..0000000 --- a/items/icons/placeholder/fish_coin.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://trqvs4dgd4rt" -path="res://.godot/imported/fish_coin.svg-9d00b5918d94a555938a84ee65e06386.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://items/icons/placeholder/fish_coin.svg" -dest_files=["res://.godot/imported/fish_coin.svg-9d00b5918d94a555938a84ee65e06386.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/items/icons/placeholder/reel_speed_upgrade.svg b/items/icons/placeholder/reel_speed_upgrade.svg deleted file mode 100644 index a92d9fb..0000000 --- a/items/icons/placeholder/reel_speed_upgrade.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/items/icons/placeholder/reel_speed_upgrade.svg.import b/items/icons/placeholder/reel_speed_upgrade.svg.import deleted file mode 100644 index 211c08f..0000000 --- a/items/icons/placeholder/reel_speed_upgrade.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c2hal1gkmnuqa" -path="res://.godot/imported/reel_speed_upgrade.svg-6bce0cfdbf18895938d0bcb4850467c8.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://items/icons/placeholder/reel_speed_upgrade.svg" -dest_files=["res://.godot/imported/reel_speed_upgrade.svg-6bce0cfdbf18895938d0bcb4850467c8.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/items/icons/shop/32_currency.png b/items/icons/shop/32_currency.png new file mode 100644 index 0000000..de2a80c Binary files /dev/null and b/items/icons/shop/32_currency.png differ diff --git a/items/icons/shop/32_currency.png.import b/items/icons/shop/32_currency.png.import new file mode 100644 index 0000000..1614c30 --- /dev/null +++ b/items/icons/shop/32_currency.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bf3jlmqhmmuge" +path="res://.godot/imported/32_currency.png-c07c9577bae5b0fbbae9513440339b6f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/shop/32_currency.png" +dest_files=["res://.godot/imported/32_currency.png-c07c9577bae5b0fbbae9513440339b6f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/shop/64_power_plus.png b/items/icons/shop/64_power_plus.png new file mode 100644 index 0000000..8aacdbc Binary files /dev/null and b/items/icons/shop/64_power_plus.png differ diff --git a/items/icons/shop/64_power_plus.png.import b/items/icons/shop/64_power_plus.png.import new file mode 100644 index 0000000..c82907d --- /dev/null +++ b/items/icons/shop/64_power_plus.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://xh2g4eytrjep" +path="res://.godot/imported/64_power_plus.png-98b7e06d3090a4411d56e2046b8c9d4b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/shop/64_power_plus.png" +dest_files=["res://.godot/imported/64_power_plus.png-98b7e06d3090a4411d56e2046b8c9d4b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/shop/64_speed_plus.png b/items/icons/shop/64_speed_plus.png new file mode 100644 index 0000000..bd605fe Binary files /dev/null and b/items/icons/shop/64_speed_plus.png differ diff --git a/items/icons/shop/64_speed_plus.png.import b/items/icons/shop/64_speed_plus.png.import new file mode 100644 index 0000000..6d330de --- /dev/null +++ b/items/icons/shop/64_speed_plus.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dk6yx5cbpblo6" +path="res://.godot/imported/64_speed_plus.png-c695fb4021c17dfa94ed7aa48ce262c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/shop/64_speed_plus.png" +dest_files=["res://.godot/imported/64_speed_plus.png-c695fb4021c17dfa94ed7aa48ce262c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/item_data.gd b/items/item_data.gd index f556fec..a34cec0 100644 --- a/items/item_data.gd +++ b/items/item_data.gd @@ -16,6 +16,8 @@ enum Category { @export var display_name: String @export_multiline var description: String @export var category: Category = Category.UTILITY +@export var bait_tags: Array[StringName] = [] +@export var lure_effects: Array[StringName] = [] @export var icon: Texture2D @export var stackable: bool = false @export_range(1, 999, 1) var max_stack: int = 1 @@ -35,3 +37,11 @@ func is_valid() -> bool: func get_category_name() -> String: return Category.keys()[category].to_lower() + + +func is_bait() -> bool: + return category == Category.BAIT and not bait_tags.is_empty() + + +func is_lure() -> bool: + return category == Category.LURE and equippable diff --git a/jobs/job_catalog.gd b/jobs/job_catalog.gd new file mode 100644 index 0000000..009a152 --- /dev/null +++ b/jobs/job_catalog.gd @@ -0,0 +1,416 @@ +class_name JobCatalog +extends RefCounted + +const FishDataType = preload("res://fish/fish_data.gd") + +enum Kind { + CATCH_TOTAL, + CATCH_WATER, + SELL_TOTAL, + CATCH_QUALITY, + CATCH_PHASE, + CATCH_WEATHER, + CATCH_SPECIES, + REACH_LEVEL, + DISCOVER_SPECIES, + MASTER_QUALITIES, +} + +const DAILY_JOB_COUNT: int = 4 +const WEATHER_SEGMENT_HOURS: float = ( + WorldWeatherService.DAILY_PLAN_SEGMENT_HOURS +) +const WEATHER_SEGMENT_COUNT: int = ( + WorldWeatherService.DAILY_PLAN_SEGMENT_COUNT +) +const MAX_JOB_REWARD_COINS: int = 100000 +const MAX_JOB_REWARD_EXPERIENCE: int = 10000 + + +static func generate_daily_jobs( + plan_id: String, + candidates: Array[FishDataType], + allow_weather_jobs: bool = true, +) -> Array[Dictionary]: + var rng := RandomNumberGenerator.new() + rng.seed = plan_id.hash() + var jobs: Array[Dictionary] = [ + _job( + "fresh_catches", + "fresh catch delivery", + "catch 5 fresh water fish", + Kind.CATCH_WATER, + 5, + 35, + 50, + {"water_type": int(WaterType.Type.FRESH_WATER)}, + ), + _job( + "sell_fish", + "market supply", + "sell 5 fish", + Kind.SELL_TOTAL, + 5, + 40, + 50, + ), + ] + var optional: Array[Dictionary] = [ + _job( + "quality_catch", + "quality check", + "catch an impressive fish or better", + Kind.CATCH_QUALITY, + 1, + 55, + 75, + {"minimum_quality": int(FishQuality.Tier.IMPRESSIVE)}, + ), + _job( + "night_catch", + "night shift", + "catch 3 fish at night", + Kind.CATCH_PHASE, + 3, + 35, + 50, + {"phase": int(WorldTimeService.Phase.NIGHT)}, + ), + ] + if allow_weather_jobs: + optional.append(_job( + "fog_catch", + "through the fog", + "catch 2 fish in foggy weather", + Kind.CATCH_WEATHER, + 2, + 65, + 75, + {"weather": int(WorldWeatherService.Weather.FOGGY)}, + )) + var species: Array[FishDataType] = [] + for fish: FishDataType in candidates: + if fish != null and fish.is_selectable(): + species.append(fish) + if not species.is_empty(): + species.sort_custom( + func(a: FishDataType, b: FishDataType) -> bool: + return String(a.id) < String(b.id) + ) + var species_index: int = rng.randi_range(0, species.size() - 1) + var selected: FishDataType = species[species_index] + optional.append(_job( + "species_%s" % String(selected.id), + "specific request", + "catch 3 %s" % selected.display_name.to_lower(), + Kind.CATCH_SPECIES, + 3, + 45, + 50, + {"fish_id": String(selected.id)}, + )) + _shuffle(optional, rng) + while jobs.size() < DAILY_JOB_COUNT and not optional.is_empty(): + jobs.append(optional.pop_back()) + return jobs + + +static func generate_weather_schedule( + plan_id: String, + jobs: Array[Dictionary], + anchor_index: int = 0, +) -> Array[Dictionary]: + anchor_index = clampi(anchor_index, 0, WEATHER_SEGMENT_COUNT - 1) + var required: Array[int] = [] + for job: Dictionary in jobs: + if int(job.get("kind", -1)) != Kind.CATCH_WEATHER: + continue + var weather: int = int(job.get("weather", -1)) + if WorldWeatherService.is_valid_weather(weather) and weather not in required: + required.append(weather) + var rng := RandomNumberGenerator.new() + rng.seed = plan_id.hash() ^ 0x57454154 + var weather_values: Array[int] = [] + for index: int in WEATHER_SEGMENT_COUNT: + var roll: float = rng.randf() + var weather: int = int(WorldWeatherService.Weather.SUNNY) + if roll >= 0.42 and roll < 0.70: + weather = int(WorldWeatherService.Weather.CLOUDY) + elif roll >= 0.70 and roll < 0.88: + weather = int(WorldWeatherService.Weather.RAINY) + elif roll >= 0.88: + weather = int(WorldWeatherService.Weather.FOGGY) + weather_values.append(weather) + for requirement_index: int in required.size(): + var early_index: int = mini( + anchor_index + requirement_index, + WEATHER_SEGMENT_COUNT - 2, + ) + var remaining_segments: int = WEATHER_SEGMENT_COUNT - early_index + var later_index: int = mini( + early_index + maxi( + 1, floori(float(remaining_segments) / 2.0) + ), + WEATHER_SEGMENT_COUNT - 1, + ) + weather_values[early_index] = required[requirement_index] + weather_values[later_index] = required[requirement_index] + var schedule: Array[Dictionary] = [] + for index: int in WEATHER_SEGMENT_COUNT: + schedule.append({ + "start_hour": fposmod( + WorldTimeService.DAY_START_HOUR + + float(index) * WEATHER_SEGMENT_HOURS, + WorldTimeService.HOURS_PER_DAY, + ), + "weather": weather_values[index], + }) + return schedule + + +static func lifetime_chains( + registered_species_count: int, +) -> Array[Dictionary]: + var chains: Array[Dictionary] = [ + _chain("catch", Kind.CATCH_TOTAL, [100, 1000, 5000], [ + [150, 250], [700, 1000], [2500, 3000], + ]), + _chain("sell", Kind.SELL_TOTAL, [50, 500, 2500], [ + [125, 200], [600, 800], [2200, 2400], + ]), + _chain("level", Kind.REACH_LEVEL, [5, 10, 25, 50], [ + [100, 150], [250, 350], [1000, 1200], [3000, 3500], + ]), + ] + if registered_species_count > 0: + chains.append(_chain( + "discover", + Kind.DISCOVER_SPECIES, + [registered_species_count], + [[500, 750]], + )) + chains.append(_chain( + "master", + Kind.MASTER_QUALITIES, + [registered_species_count], + [[1500, 2000]], + )) + return chains + + +static func visible_lifetime_jobs( + registered_species_count: int, + claimed_ids: Array[String], +) -> Array[Dictionary]: + var visible: Array[Dictionary] = [] + for chain: Dictionary in lifetime_chains(registered_species_count): + var targets: Array = chain.get("targets", []) + var rewards: Array = chain.get("rewards", []) + for tier_index: int in targets.size(): + var job_id: String = "%s_%d" % [ + str(chain.get("id", "")), + int(targets[tier_index]), + ] + if job_id in claimed_ids: + continue + var reward: Array = rewards[tier_index] + visible.append(_job( + job_id, + _lifetime_title(str(chain.get("id", "")), int(targets[tier_index])), + _lifetime_description( + str(chain.get("id", "")), int(targets[tier_index]) + ), + int(chain.get("kind", Kind.CATCH_TOTAL)) as Kind, + int(targets[tier_index]), + int(reward[0]), + int(reward[1]), + )) + break + return visible + + +static func weather_requirements(jobs: Array[Dictionary]) -> Array[int]: + var result: Array[int] = [] + for job: Dictionary in jobs: + if int(job.get("kind", -1)) != Kind.CATCH_WEATHER: + continue + var weather: int = int(job.get("weather", -1)) + if WorldWeatherService.is_valid_weather(weather) and weather not in result: + result.append(weather) + return result + + +static func is_valid_job(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var job: Dictionary = value + var kind: int = int(job.get("kind", -1)) + if not ( + typeof(job.get("id")) == TYPE_STRING + and not str(job["id"]).is_empty() + and str(job["id"]).length() <= 96 + and typeof(job.get("title")) == TYPE_STRING + and str(job["title"]).length() <= 96 + and typeof(job.get("description")) == TYPE_STRING + and str(job["description"]).length() <= 160 + and is_bounded_integer( + job.get("kind"), Kind.CATCH_TOTAL, Kind.MASTER_QUALITIES + ) + and kind >= Kind.CATCH_TOTAL + and kind <= Kind.MASTER_QUALITIES + and is_bounded_integer(job.get("target"), 1, 1000000000) + and is_bounded_integer( + job.get("fish_coin"), 0, MAX_JOB_REWARD_COINS + ) + and is_bounded_integer( + job.get("experience"), 0, MAX_JOB_REWARD_EXPERIENCE + ) + ): + return false + match kind: + Kind.CATCH_WATER: + var water_type: int = int(job.get("water_type", -1)) + return ( + is_bounded_integer( + job.get("water_type"), + WaterType.Type.FRESH_WATER, + WaterType.Type.OTHER, + ) + and water_type >= WaterType.Type.FRESH_WATER + and water_type <= WaterType.Type.OTHER + ) + Kind.CATCH_QUALITY: + return ( + is_bounded_integer( + job.get("minimum_quality"), + FishQuality.Tier.BORING, + FishQuality.Tier.SHINY, + ) + and FishQuality.is_valid(int(job.get("minimum_quality", -1))) + ) + Kind.CATCH_PHASE: + var phase: int = int(job.get("phase", -1)) + return ( + is_bounded_integer( + job.get("phase"), + WorldTimeService.Phase.DAWN, + WorldTimeService.Phase.NIGHT, + ) + and phase >= WorldTimeService.Phase.DAWN + and phase <= WorldTimeService.Phase.NIGHT + ) + Kind.CATCH_WEATHER: + return ( + is_bounded_integer( + job.get("weather"), + WorldWeatherService.Weather.SUNNY, + WorldWeatherService.Weather.FOGGY, + ) + and WorldWeatherService.is_valid_weather( + int(job.get("weather", -1)) + ) + ) + Kind.CATCH_SPECIES: + return ( + typeof(job.get("fish_id")) == TYPE_STRING + and not str(job.get("fish_id", "")).is_empty() + and str(job.get("fish_id", "")).length() <= 96 + ) + return true + + +static func is_bounded_integer( + value: Variant, + minimum: int, + maximum: int, +) -> bool: + if typeof(value) == TYPE_INT: + return int(value) >= minimum and int(value) <= maximum + if typeof(value) != TYPE_FLOAT: + return false + var number: float = float(value) + return ( + is_finite(number) + and number >= float(minimum) + and number <= float(maximum) + and is_equal_approx(number, round(number)) + ) + + +static func is_valid_weather_schedule(value: Variant) -> bool: + return WorldWeatherService.is_valid_daily_plan_schedule(value) + + +static func _job( + id: String, + title: String, + description: String, + kind: Kind, + target: int, + fish_coin: int, + experience: int, + extra: Dictionary = {}, +) -> Dictionary: + var result: Dictionary = { + "id": id, + "title": title, + "description": description, + "kind": int(kind), + "target": target, + "fish_coin": fish_coin, + "experience": experience, + } + result.merge(extra, true) + return result + + +static func _chain( + id: String, + kind: Kind, + targets: Array[int], + rewards: Array, +) -> Dictionary: + return { + "id": id, + "kind": int(kind), + "targets": targets, + "rewards": rewards, + } + + +static func _lifetime_title(chain_id: String, target: int) -> String: + match chain_id: + "catch": + return "seasoned angler" + "sell": + return "market regular" + "level": + return "reach level %d" % target + "discover": + return "complete the catalog" + "master": + return "quality master" + return "long-term job" + + +static func _lifetime_description(chain_id: String, target: int) -> String: + match chain_id: + "catch": + return "catch %d fish" % target + "sell": + return "sell %d fish" % target + "level": + return "reach player level %d" % target + "discover": + return "discover all %d cataloged fish" % target + "master": + return "collect every quality of all %d fish" % target + return "keep fishing" + + +static func _shuffle(values: Array[Dictionary], rng: RandomNumberGenerator) -> void: + for index: int in range(values.size() - 1, 0, -1): + var swap_index: int = rng.randi_range(0, index) + var value: Dictionary = values[index] + values[index] = values[swap_index] + values[swap_index] = value diff --git a/jobs/job_catalog.gd.uid b/jobs/job_catalog.gd.uid new file mode 100644 index 0000000..f54ab48 --- /dev/null +++ b/jobs/job_catalog.gd.uid @@ -0,0 +1 @@ +uid://c52vqhdties3o diff --git a/jobs/player_job_service.gd b/jobs/player_job_service.gd new file mode 100644 index 0000000..18d6f89 --- /dev/null +++ b/jobs/player_job_service.gd @@ -0,0 +1,814 @@ +class_name PlayerJobService +extends Node + +const FishCatchType = preload("res://fish/fish_catch.gd") +const FishDataType = preload("res://fish/fish_data.gd") +const FishPoolType = preload("res://fish/fish_pool.gd") + +const FORMAT_VERSION: int = 1 +const MAX_PROGRESS_VALUE: int = 1000000000 +const MAX_PENDING_REWARDS: int = 64 +const DAILY_REFRESH_HOUR: float = WorldTimeService.DAY_START_HOUR + +signal changed +signal board_changed +signal reward_claimed(title: String, fish_coin: int, experience: int) +signal status_changed(message: String) + +var _wallet: PlayerWallet +var _experience: PlayerExperience +var _collection: CollectionLog +var _catalog: FishPoolType +var _world_time: WorldTimeService +var _world_weather: WorldWeatherService +var _session: NetworkSession +var _save_manager: PlayerSaveManager +var _network_fishing: NetworkFishingService +var _network_sale: NetworkSaleService + +var _progression_ready: bool = false +var _host_board: Dictionary = {} +var _active_board: Dictionary = {} +var _active_plan_id: String = "" +var _daily_progress: Dictionary[String, int] = {} +var _daily_completions: Dictionary[String, int] = {} +var _pending_rewards: Array[Dictionary] = [] +var _lifetime_claimed: Array[String] = [] +var _total_catches: int = 0 +var _total_sold: int = 0 +var _daily_clock_hour: float = WorldTimeService.DEFAULT_START_HOUR + + +func setup( + wallet: PlayerWallet, + experience: PlayerExperience, + collection: CollectionLog, + catalog: FishPoolType, + world_time: WorldTimeService, + world_weather: WorldWeatherService, + session: NetworkSession, +) -> void: + _wallet = wallet + _experience = experience + _collection = collection + _catalog = catalog + _world_time = world_time + _world_weather = world_weather + _session = session + _daily_clock_hour = _world_time.get_time_hours() + _world_time.natural_time_advanced.connect(_on_natural_time_advanced) + _session.state_changed.connect(_on_session_state_changed) + _collection.collection_changed.connect(_on_current_state_changed) + _experience.experience_changed.connect(_on_experience_changed) + + +func bind_authoritative_services( + network_fishing: NetworkFishingService, + network_sale: NetworkSaleService, +) -> void: + _network_fishing = network_fishing + _network_sale = network_sale + if not _network_fishing.local_catch_received.is_connected( + _on_authoritative_catch + ): + _network_fishing.local_catch_received.connect(_on_authoritative_catch) + if not _network_sale.local_sale_finished.is_connected( + _on_authoritative_sale_finished + ): + _network_sale.local_sale_finished.connect( + _on_authoritative_sale_finished + ) + + +func set_save_manager(save_manager: PlayerSaveManager) -> void: + _save_manager = save_manager + + +func begin_progression_session() -> void: + _progression_ready = true + if _session.is_host(): + _activate_host_board() + changed.emit() + + +func end_progression_session() -> void: + _progression_ready = false + _active_board = {} + changed.emit() + + +func get_daily_jobs() -> Array[Dictionary]: + var result: Array[Dictionary] = [] + var jobs: Array = _active_board.get("jobs", []) + for value: Variant in jobs: + if typeof(value) != TYPE_DICTIONARY: + continue + var job: Dictionary = (value as Dictionary).duplicate(true) + var job_id: String = str(job.get("id", "")) + job["progress"] = mini( + int(_daily_progress.get(job_id, 0)), int(job.get("target", 1)) + ) + job["completed_count"] = int(_daily_completions.get(job_id, 0)) + var pending: Array[Dictionary] = _pending_for_job( + _active_plan_id, job_id + ) + job["pending_count"] = pending.size() + job["claim_id"] = ( + str(pending[0].get("claim_id", "")) if not pending.is_empty() else "" + ) + job["claimable"] = not pending.is_empty() + result.append(job) + return result + + +func get_lifetime_jobs() -> Array[Dictionary]: + var result: Array[Dictionary] = JobCatalog.visible_lifetime_jobs( + _registered_species_count(), _lifetime_claimed + ) + for job: Dictionary in result: + var progress: int = _lifetime_progress(job) + job["progress"] = mini(progress, int(job.get("target", 1))) + job["complete"] = progress >= int(job.get("target", 1)) + job["claimable"] = bool(job["complete"]) + return result + + +func get_pending_rewards() -> Array[Dictionary]: + return _pending_rewards.duplicate(true) + + +func get_forecast() -> Array[Dictionary]: + var schedule: Array = _active_board.get("weather_schedule", []) + var result: Array[Dictionary] = [] + for value: Variant in schedule: + if typeof(value) == TYPE_DICTIONARY: + result.append((value as Dictionary).duplicate(true)) + return result + + +func get_plan_id() -> String: + return _active_plan_id + + +func has_active_board() -> bool: + return validate_board(_active_board) + + +func get_time_until_refresh_text() -> String: + if _active_plan_id.is_empty() or _world_time == null: + return "daily jobs unavailable" + var elapsed: float = fposmod( + _daily_clock_hour - DAILY_REFRESH_HOUR, + WorldTimeService.HOURS_PER_DAY, + ) + var hours_remaining: float = WorldTimeService.HOURS_PER_DAY - elapsed + var real_seconds: int = ceili( + hours_remaining / WorldTimeService.HOURS_PER_REAL_SECOND + ) + return "refreshes in %d:%02d" % [ + floori(float(real_seconds) / 60.0), real_seconds % 60, + ] + + +func claim(claim_id: String) -> bool: + if claim_id.is_empty() or _wallet == null or _experience == null: + return false + for index: int in _pending_rewards.size(): + var reward: Dictionary = _pending_rewards[index] + if str(reward.get("claim_id", "")) != claim_id: + continue + return _claim_pending_reward(index, reward) + for lifetime_job: Dictionary in get_lifetime_jobs(): + if str(lifetime_job.get("id", "")) != claim_id: + continue + if not bool(lifetime_job.get("claimable", false)): + return false + return _claim_lifetime_reward(lifetime_job) + return false + + +func get_host_board_network_data() -> Dictionary: + var board := _host_board.duplicate(true) + if not board.is_empty(): + board["daily_clock_hour"] = _daily_clock_hour + return board + + +func apply_remote_board(board: Dictionary) -> bool: + if not validate_board(board) or not _matches_canonical_board(board): + return false + var plan_id: String = str(board.get("plan_id", "")) + if plan_id != _active_plan_id: + _expire_incomplete_daily_jobs() + _active_plan_id = plan_id + _daily_progress.clear() + _daily_completions.clear() + _active_board = board.duplicate(true) + if board.has("daily_clock_hour"): + _daily_clock_hour = float(board["daily_clock_hour"]) + changed.emit() + return true + + +func clear_remote_board() -> void: + if _session != null and _session.is_host(): + _activate_host_board() + return + _active_board = {} + changed.emit() + + +func to_save_data() -> Dictionary: + var progress: Dictionary = {} + for job_id: String in _daily_progress: + progress[job_id] = _daily_progress[job_id] + var completions: Dictionary = {} + for job_id: String in _daily_completions: + completions[job_id] = _daily_completions[job_id] + return { + "format_version": FORMAT_VERSION, + "host_board": _host_board.duplicate(true), + "active_plan_id": _active_plan_id, + "daily_progress": progress, + "daily_completions": completions, + "daily_clock_hour": _daily_clock_hour, + "pending_rewards": _pending_rewards.duplicate(true), + "lifetime_claimed": _lifetime_claimed.duplicate(), + "statistics": { + "fish_caught": _total_catches, + "fish_sold": _total_sold, + }, + } + + +func restore_from_save_data(data: Dictionary) -> bool: + if not validate_save_data(data): + return false + var received_board: Dictionary = _active_board.duplicate(true) + _host_board = (data.get("host_board", {}) as Dictionary).duplicate(true) + _active_plan_id = str(data.get("active_plan_id", "")) + _daily_progress.clear() + var progress: Dictionary = data.get("daily_progress", {}) + for key: Variant in progress: + _daily_progress[str(key)] = int(progress[key]) + _daily_completions.clear() + var completions: Dictionary = data.get("daily_completions", {}) + for key: Variant in completions: + _daily_completions[str(key)] = mini(int(completions[key]), 1) + _daily_clock_hour = float(data.get( + "daily_clock_hour", + _world_time.get_time_hours() + if _world_time != null + else WorldTimeService.DEFAULT_START_HOUR, + )) + _pending_rewards.clear() + var rewards: Array = data.get("pending_rewards", []) + var restored_daily_rewards: Dictionary[String, bool] = {} + for value: Variant in rewards: + var reward: Dictionary = value + var reward_key: String = "%s/%s" % [ + str(reward.get("source_plan_id", "")), + str(reward.get("source_job_id", "")), + ] + if restored_daily_rewards.has(reward_key): + continue + restored_daily_rewards[reward_key] = true + _pending_rewards.append(reward.duplicate(true)) + _lifetime_claimed.clear() + var claimed: Array = data.get("lifetime_claimed", []) + for value: Variant in claimed: + _lifetime_claimed.append(str(value)) + var statistics: Dictionary = data.get("statistics", {}) + _total_catches = int(statistics.get("fish_caught", 0)) + _total_sold = int(statistics.get("fish_sold", 0)) + if _session != null and _session.is_host(): + _active_board = _host_board.duplicate(true) + _active_plan_id = str(_host_board.get("plan_id", "")) + elif ( + _session != null + and _session.is_joined_client() + and not received_board.is_empty() + ): + apply_remote_board(received_board) + _apply_host_weather_plan() + changed.emit() + return true + + +func reset_to_defaults() -> void: + _host_board = {} + _active_board = {} + _active_plan_id = "" + _daily_progress.clear() + _daily_completions.clear() + _pending_rewards.clear() + _lifetime_claimed.clear() + _total_catches = 0 + _total_sold = 0 + _daily_clock_hour = WorldTimeService.DEFAULT_START_HOUR + changed.emit() + + +static func default_save_data() -> Dictionary: + return { + "format_version": FORMAT_VERSION, + "host_board": {}, + "active_plan_id": "", + "daily_progress": {}, + "daily_completions": {}, + "daily_clock_hour": WorldTimeService.DEFAULT_START_HOUR, + "pending_rewards": [], + "lifetime_claimed": [], + "statistics": {"fish_caught": 0, "fish_sold": 0}, + } + + +static func validate_board(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var board: Dictionary = value + if ( + typeof(board.get("plan_id")) != TYPE_STRING + or str(board.get("plan_id", "")).is_empty() + or str(board.get("plan_id", "")).length() > 96 + or not JobCatalog.is_bounded_integer( + board.get("cycle"), 0, MAX_PROGRESS_VALUE + ) + or not JobCatalog.is_bounded_integer( + board.get("schedule_anchor_index"), + 0, + JobCatalog.WEATHER_SEGMENT_COUNT - 1, + ) + or typeof(board.get("jobs")) != TYPE_ARRAY + or not JobCatalog.is_valid_weather_schedule( + board.get("weather_schedule", []) + ) + ): + return false + var jobs: Array = board.get("jobs", []) + if jobs.is_empty() or jobs.size() > 8: + return false + var seen: Dictionary[String, bool] = {} + for job_value: Variant in jobs: + if not JobCatalog.is_valid_job(job_value): + return false + var job: Dictionary = job_value + var job_id: String = str(job.get("id", "")) + if seen.has(job_id): + return false + seen[job_id] = true + return true + + +static func validate_save_data(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var data: Dictionary = value + if ( + not JobCatalog.is_bounded_integer( + data.get("format_version"), FORMAT_VERSION, FORMAT_VERSION + ) + or typeof(data.get("host_board")) != TYPE_DICTIONARY + or typeof(data.get("active_plan_id")) != TYPE_STRING + or typeof(data.get("daily_progress")) != TYPE_DICTIONARY + or typeof(data.get("daily_completions")) != TYPE_DICTIONARY + or typeof(data.get("pending_rewards")) != TYPE_ARRAY + or typeof(data.get("lifetime_claimed")) != TYPE_ARRAY + or typeof(data.get("statistics")) != TYPE_DICTIONARY + ): + return false + var host_board: Dictionary = data.get("host_board", {}) + if not host_board.is_empty() and not validate_board(host_board): + return false + if ( + data.has("daily_clock_hour") + and ( + typeof(data["daily_clock_hour"]) not in [TYPE_FLOAT, TYPE_INT] + or not is_finite(float(data["daily_clock_hour"])) + or float(data["daily_clock_hour"]) < 0.0 + or float(data["daily_clock_hour"]) >= WorldTimeService.HOURS_PER_DAY + ) + ): + return false + var progress: Dictionary = data.get("daily_progress", {}) + if progress.size() > 8: + return false + for key: Variant in progress: + if ( + typeof(key) != TYPE_STRING + or str(key).is_empty() + or str(key).length() > 96 + or not JobCatalog.is_bounded_integer( + progress[key], 0, MAX_PROGRESS_VALUE + ) + ): + return false + var completions: Dictionary = data.get("daily_completions", {}) + if completions.size() > 8: + return false + for key: Variant in completions: + if ( + typeof(key) != TYPE_STRING + or str(key).is_empty() + or str(key).length() > 96 + or not JobCatalog.is_bounded_integer( + completions[key], 0, MAX_PROGRESS_VALUE + ) + ): + return false + var rewards: Array = data.get("pending_rewards", []) + if rewards.size() > MAX_PENDING_REWARDS: + return false + for reward: Variant in rewards: + if not _valid_reward(reward): + return false + var claimed: Array = data.get("lifetime_claimed", []) + if claimed.size() > 64 or not _valid_string_array(claimed, 96): + return false + var statistics: Dictionary = data.get("statistics", {}) + return ( + JobCatalog.is_bounded_integer( + statistics.get("fish_caught"), 0, MAX_PROGRESS_VALUE + ) + and JobCatalog.is_bounded_integer( + statistics.get("fish_sold"), 0, MAX_PROGRESS_VALUE + ) + ) + + +func _activate_host_board() -> void: + if _host_board.is_empty(): + _generate_host_board(0) + else: + _active_board = _host_board.duplicate(true) + _switch_plan(str(_host_board.get("plan_id", ""))) + _apply_host_weather_plan() + board_changed.emit() + changed.emit() + + +func _generate_host_board(cycle: int) -> void: + _expire_incomplete_daily_jobs() + var random_suffix: String = Crypto.new().generate_random_bytes(12).hex_encode() + var plan_id: String = "daily:%d:%s" % [cycle, random_suffix] + var candidates: Array[FishDataType] = [] + if _catalog != null: + for fish: FishDataType in _catalog.candidates: + if fish != null: + candidates.append(fish) + var schedule_anchor_index: int = _current_weather_segment() + var allow_weather_jobs: bool = ( + JobCatalog.WEATHER_SEGMENT_COUNT - schedule_anchor_index >= 2 + ) + var jobs: Array[Dictionary] = JobCatalog.generate_daily_jobs( + plan_id, candidates, allow_weather_jobs + ) + _host_board = { + "plan_id": plan_id, + "cycle": cycle, + "schedule_anchor_index": schedule_anchor_index, + "jobs": jobs, + "weather_schedule": JobCatalog.generate_weather_schedule( + plan_id, jobs, schedule_anchor_index + ), + } + _active_board = _host_board.duplicate(true) + _switch_plan(plan_id) + _apply_host_weather_plan() + board_changed.emit() + changed.emit() + + +func _switch_plan(plan_id: String) -> void: + if plan_id == _active_plan_id: + return + _active_plan_id = plan_id + _daily_progress.clear() + _daily_completions.clear() + + +func _apply_host_weather_plan() -> void: + if ( + _world_weather == null + or _world_time == null + or _session == null + or not _session.is_host() + or _host_board.is_empty() + ): + return + var schedule: Array = _host_board.get("weather_schedule", []) + _world_weather.configure_daily_plan( + str(_host_board.get("plan_id", "")), schedule, _world_time + ) + + +func _on_natural_time_advanced(hours: float) -> void: + if not is_finite(hours) or hours <= 0.0: + return + var previous_hour := _daily_clock_hour + _daily_clock_hour = fposmod( + _daily_clock_hour + hours, + WorldTimeService.HOURS_PER_DAY, + ) + if ( + _progression_ready + and _session != null + and _session.is_host() + and _crossed_daily_refresh(previous_hour, _daily_clock_hour) + ): + var cycle: int = int(_host_board.get("cycle", -1)) + 1 + _generate_host_board(maxi(cycle, 0)) + + +func _on_session_state_changed(state: NetworkSession.State) -> void: + if not _progression_ready: + return + if state in [NetworkSession.State.PRIVATE_HOST, NetworkSession.State.OPEN_HOST]: + _activate_host_board() + elif state == NetworkSession.State.JOINED_CLIENT: + _active_board = {} + changed.emit() + elif state in [ + NetworkSession.State.INACTIVE, + NetworkSession.State.DISCONNECTING, + NetworkSession.State.CONNECTION_FAILED, + NetworkSession.State.SERVER_LOST, + ]: + clear_remote_board() + + +func _on_authoritative_catch(fish_catch: FishCatchType) -> void: + if not _progression_ready or fish_catch == null or not fish_catch.is_valid(): + return + _total_catches = mini(_total_catches + 1, MAX_PROGRESS_VALUE) + _update_daily_for_catch(fish_catch) + changed.emit() + + +func _on_authoritative_sale_finished( + _request_id: String, + accepted: bool, + _message: String, + catch_ids: Array[StringName], + _payout: int, +) -> void: + if not _progression_ready or not accepted or catch_ids.is_empty(): + return + _total_sold = mini(_total_sold + catch_ids.size(), MAX_PROGRESS_VALUE) + for job: Dictionary in get_daily_jobs(): + if int(job.get("kind", -1)) == JobCatalog.Kind.SELL_TOTAL: + _advance_daily(job, catch_ids.size()) + changed.emit() + + +func _update_daily_for_catch(fish_catch: FishCatchType) -> void: + for job: Dictionary in get_daily_jobs(): + var matches: bool = false + match int(job.get("kind", -1)): + JobCatalog.Kind.CATCH_TOTAL: + matches = true + JobCatalog.Kind.CATCH_WATER: + matches = fish_catch.fish.is_allowed_in_water( + int(job.get("water_type", WaterType.Type.OTHER)) as WaterType.Type + ) + JobCatalog.Kind.CATCH_QUALITY: + matches = fish_catch.quality >= int(job.get("minimum_quality", 0)) + JobCatalog.Kind.CATCH_PHASE: + matches = int(_world_time.get_phase()) == int(job.get("phase", -1)) + JobCatalog.Kind.CATCH_WEATHER: + matches = int(_world_weather.get_weather()) == int(job.get("weather", -1)) + JobCatalog.Kind.CATCH_SPECIES: + matches = String(fish_catch.fish_id) == str(job.get("fish_id", "")) + if matches: + _advance_daily(job, 1) + + +func _advance_daily(job: Dictionary, amount: int) -> void: + var job_id: String = str(job.get("id", "")) + if job_id.is_empty() or amount <= 0: + return + if int(_daily_completions.get(job_id, 0)) > 0: + return + var target: int = int(job.get("target", 1)) + var progress: int = mini( + int(_daily_progress.get(job_id, 0)) + amount, + target, + ) + if progress >= target and _pending_rewards.size() < MAX_PENDING_REWARDS: + _daily_completions[job_id] = 1 + var reward: Dictionary = { + "claim_id": _daily_claim_id(_active_plan_id, job_id), + "source_plan_id": _active_plan_id, + "source_job_id": job_id, + "title": str(job.get("title", "daily job")), + "fish_coin": int(job.get("fish_coin", 0)), + "experience": int(job.get("experience", 0)), + } + _pending_rewards.append(reward) + status_changed.emit("job complete — payment ready on the net") + _daily_progress[job_id] = progress + + +func _claim_pending_reward(index: int, reward: Dictionary) -> bool: + var state_snapshot: Dictionary = to_save_data() + var wallet_snapshot: int = _wallet.get_balance() + var experience_snapshot: int = _experience.get_total_experience() + _pending_rewards.remove_at(index) + if not _apply_reward(reward) or not _save_claim_transaction(): + _wallet.restore_balance(wallet_snapshot) + _experience.restore_total_experience(experience_snapshot) + restore_from_save_data(state_snapshot) + return false + _reward_claimed(reward) + return true + + +func _claim_lifetime_reward(job: Dictionary) -> bool: + var job_id: String = str(job.get("id", "")) + if job_id.is_empty() or job_id in _lifetime_claimed: + return false + var state_snapshot: Dictionary = to_save_data() + var wallet_snapshot: int = _wallet.get_balance() + var experience_snapshot: int = _experience.get_total_experience() + _lifetime_claimed.append(job_id) + if not _apply_reward(job) or not _save_claim_transaction(): + _wallet.restore_balance(wallet_snapshot) + _experience.restore_total_experience(experience_snapshot) + restore_from_save_data(state_snapshot) + return false + _reward_claimed(job) + return true + + +func _apply_reward(reward: Dictionary) -> bool: + var fish_coin: int = int(reward.get("fish_coin", 0)) + var experience: int = int(reward.get("experience", 0)) + if fish_coin > 0 and not _wallet.credit(fish_coin): + return false + if experience > 0 and not _experience.award_experience(experience): + return false + changed.emit() + return true + + +func _save_claim_transaction() -> bool: + return _save_manager != null and _save_manager.save_if_dirty() + + +func _reward_claimed(reward: Dictionary) -> void: + var title: String = str(reward.get("title", "job")) + var fish_coin: int = int(reward.get("fish_coin", 0)) + var experience: int = int(reward.get("experience", 0)) + reward_claimed.emit(title, fish_coin, experience) + status_changed.emit("payment received — %d xp" % experience) + changed.emit() + + +func _lifetime_progress(job: Dictionary) -> int: + match int(job.get("kind", -1)): + JobCatalog.Kind.CATCH_TOTAL: + return _total_catches + JobCatalog.Kind.SELL_TOTAL: + return _total_sold + JobCatalog.Kind.REACH_LEVEL: + return _experience.get_level() if _experience != null else 0 + JobCatalog.Kind.DISCOVER_SPECIES: + return _collection.get_discovered_ids().size() if _collection != null else 0 + JobCatalog.Kind.MASTER_QUALITIES: + var mastered: int = 0 + if _collection != null and _catalog != null: + for fish: FishDataType in _catalog.candidates: + if fish != null and _collection.has_mastered(fish.id): + mastered += 1 + return mastered + return 0 + + +func _registered_species_count() -> int: + var count: int = 0 + if _catalog != null: + for fish: FishDataType in _catalog.candidates: + if fish != null and fish.is_selectable(): + count += 1 + return count + + +func _expire_incomplete_daily_jobs() -> void: + _daily_progress.clear() + _daily_completions.clear() + + +func _on_current_state_changed() -> void: + if _progression_ready: + changed.emit() + + +func _on_experience_changed(_total: int, _level: int) -> void: + if _progression_ready: + changed.emit() + + +func _pending_for_job( + plan_id: String, + job_id: String, +) -> Array[Dictionary]: + var result: Array[Dictionary] = [] + for reward: Dictionary in _pending_rewards: + if ( + str(reward.get("source_plan_id", "")) == plan_id + and str(reward.get("source_job_id", "")) == job_id + ): + result.append(reward) + return result + + +func _matches_canonical_board(board: Dictionary) -> bool: + var plan_id: String = str(board.get("plan_id", "")) + var candidates: Array[FishDataType] = [] + if _catalog != null: + for fish: FishDataType in _catalog.candidates: + if fish != null: + candidates.append(fish) + var anchor_index: int = int(board.get("schedule_anchor_index", -1)) + var allow_weather_jobs: bool = ( + JobCatalog.WEATHER_SEGMENT_COUNT - anchor_index >= 2 + ) + var jobs: Array[Dictionary] = JobCatalog.generate_daily_jobs( + plan_id, candidates, allow_weather_jobs + ) + var schedule: Array[Dictionary] = JobCatalog.generate_weather_schedule( + plan_id, jobs, anchor_index + ) + return ( + board.get("jobs", []) == jobs + and board.get("weather_schedule", []) == schedule + ) + + +func _current_weather_segment() -> int: + if _world_time == null: + return 0 + var elapsed_hours: float = fposmod( + _world_time.get_time_hours() - WorldTimeService.DAY_START_HOUR, + WorldTimeService.HOURS_PER_DAY, + ) + return clampi( + floori(elapsed_hours / JobCatalog.WEATHER_SEGMENT_HOURS), + 0, + JobCatalog.WEATHER_SEGMENT_COUNT - 1, + ) + + +static func _daily_claim_id( + plan_id: String, + job_id: String, +) -> String: + return "%s/%s" % [plan_id, job_id] + + +static func _crossed_daily_refresh(previous: float, current: float) -> bool: + if current >= previous: + return previous < DAILY_REFRESH_HOUR and current >= DAILY_REFRESH_HOUR + return ( + previous < DAILY_REFRESH_HOUR + or current >= DAILY_REFRESH_HOUR + ) + + +static func _valid_string_array(values: Array, maximum_length: int) -> bool: + var seen: Dictionary[String, bool] = {} + for value: Variant in values: + if typeof(value) != TYPE_STRING: + return false + var text: String = str(value) + if text.is_empty() or text.length() > maximum_length or seen.has(text): + return false + seen[text] = true + return true + + +static func _valid_reward(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var reward: Dictionary = value + return ( + typeof(reward.get("claim_id")) == TYPE_STRING + and not str(reward.get("claim_id", "")).is_empty() + and str(reward.get("claim_id", "")).length() <= 196 + and typeof(reward.get("source_plan_id")) == TYPE_STRING + and not str(reward.get("source_plan_id", "")).is_empty() + and str(reward.get("source_plan_id", "")).length() <= 96 + and typeof(reward.get("source_job_id")) == TYPE_STRING + and not str(reward.get("source_job_id", "")).is_empty() + and str(reward.get("source_job_id", "")).length() <= 96 + and typeof(reward.get("title")) == TYPE_STRING + and str(reward.get("title", "")).length() <= 96 + and JobCatalog.is_bounded_integer( + reward.get("fish_coin"), 0, JobCatalog.MAX_JOB_REWARD_COINS + ) + and JobCatalog.is_bounded_integer( + reward.get("experience"), + 0, + JobCatalog.MAX_JOB_REWARD_EXPERIENCE, + ) + ) diff --git a/jobs/player_job_service.gd.uid b/jobs/player_job_service.gd.uid new file mode 100644 index 0000000..110c6e2 --- /dev/null +++ b/jobs/player_job_service.gd.uid @@ -0,0 +1 @@ +uid://daqtje84gqgu0 diff --git a/main/main.gd b/main/main.gd index 7a074a3..e028509 100644 --- a/main/main.gd +++ b/main/main.gd @@ -16,6 +16,9 @@ const PlayerSettingsManagerType = preload( "res://settings/player_settings_manager.gd" ) const PlayerSettingsType = preload("res://settings/player_settings.gd") +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) const TitleScreenType = preload("res://ui/title_screen.gd") const PauseMenuType = preload("res://ui/pause_menu.gd") const ItemCatalogType = preload("res://items/item_catalog.gd") @@ -35,6 +38,10 @@ const WorldPixelationPostprocessType = preload( "res://main/world_pixelation_postprocess.gd" ) const NetworkSessionType = preload("res://network/network_session.gd") +const DiscoveryClientType = preload("res://network/discovery_client.gd") +const DedicatedServerConfigType = preload( + "res://server/dedicated_server_config.gd" +) const NetworkProfilePreferencesType = preload( "res://network/network_profile_preferences.gd" ) @@ -47,6 +54,7 @@ const PlayerSpawnServiceType = preload( const NetworkFishingServiceType = preload( "res://network/network_fishing_service.gd" ) +const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") const NetworkSaleServiceType = preload( "res://network/network_sale_service.gd" ) @@ -88,8 +96,12 @@ const WorldWeatherServiceType = preload("res://world/world_weather_service.gd") const NetworkWorldWeatherServiceType = preload( "res://network/network_world_weather_service.gd" ) +const RainAmbienceType = preload("res://world/rain_ambience.gd") +const PlayerJobServiceType = preload("res://jobs/player_job_service.gd") +const NetworkJobServiceType = preload("res://network/network_job_service.gd") const TITLE_MUSIC_SILENCE_DB: float = -80.0 +const TIME_CROSSING_EPSILON_HOURS: float = 0.000001 const PLAYER_MENU_PATTERN_SCALE: float = 0.85 const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0) const SHOP_PATTERN_SCALE: float = 1.75 @@ -109,8 +121,12 @@ const SHOP_PATTERN_SCALE: float = 1.75 @onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery @onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager @onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager +@onready var _controller_mapping_manager: ControllerMappingManagerType = ( + %ControllerMappingManager +) @onready var _interface_fonts: InterfaceFontController = %InterfaceFontController @onready var _title_music: AudioStreamPlayer = %TitleMusic +@onready var _dusk_music: AudioStreamPlayer = %DuskMusic @onready var _ui_pixelation: UIPixelationPresenterType = %UIPresentation @onready var _pixelation_reset: PixelationResetOverlayType = ( %PixelationResetOverlay @@ -119,6 +135,7 @@ const SHOP_PATTERN_SCALE: float = 1.75 %WorldPixelationPostprocess ) @onready var _network_session: NetworkSessionType = %NetworkSession +@onready var _discovery: DiscoveryClientType = %DiscoveryClient @onready var _world_time: WorldTimeServiceType = %WorldTimeService @onready var _world_time_visuals: WorldTimeVisualControllerType = ( %WorldTimeVisualController @@ -130,6 +147,8 @@ const SHOP_PATTERN_SCALE: float = 1.75 @onready var _network_world_weather: NetworkWorldWeatherServiceType = ( %NetworkWorldWeatherService ) +@onready var _player_jobs: PlayerJobServiceType = %PlayerJobService +@onready var _network_jobs: NetworkJobServiceType = %NetworkJobService @onready var _data_root: PlayerDataRoot = %PlayerDataRoot @onready var _identity_backups: IdentityBackupService = %IdentityBackupService @onready var _network_profile: NetworkProfilePreferencesType = ( @@ -180,6 +199,7 @@ var _shop_interaction: FishingShopInteractionType var _title_music_tween: Tween var _title_music_transition_generation: int = 0 var _title_music_requested: bool = false +var _dusk_music_played_for_natural_day: bool = false var _quit_in_progress: bool = false var _join_requested_from_title: bool = false var _join_requested_from_pause: bool = false @@ -190,16 +210,34 @@ var _server_trust_dialog: ConfirmationDialog var _pending_trust_changed: bool = false var _identity_notice_dialog: AcceptDialog var _data_setup_dialog: ConfirmationDialog +var _data_setup_choose_button: Button +var _data_setup_current_button: Button var _data_folder_dialog: FileDialog var _data_folder_picker_generation: int = 0 var _restore_data_setup_after_picker: bool = false var _application_initialized := false var _pending_existing_root_path := "" var _local_recovery_attempt_id: String = "" +var _dedicated_runtime: bool = false + +@onready var _shoreline_ambience: ShorelineAmbience = %ShorelineAmbience +var _rain_ambience: RainAmbienceType func _ready() -> void: + _dedicated_runtime = _is_dedicated_server_runtime() + if _dedicated_runtime: + call_deferred("_start_dedicated_server") + return DisplayServer.window_set_title("NETfishing") + _rain_ambience = RainAmbienceType.new() + _rain_ambience.name = "RainAmbience" + add_child(_rain_ambience) + _rain_ambience.configure(_world_weather) + _shoreline_ambience.configure( + _player, + _test_world.get_saltwater_shoreline_mesh(), + ) var menu_pattern_material := ( _player_menu_backdrop.material as ShaderMaterial ) @@ -237,6 +275,91 @@ func _ready() -> void: _show_data_root_setup() +func _is_dedicated_server_runtime() -> bool: + if OS.has_feature("dedicated_server"): + return true + return "--dedicated-server" in OS.get_cmdline_user_args() + + +func _start_dedicated_server() -> void: + var config: DedicatedServerConfigType = DedicatedServerConfigType.from_runtime() + if not config.is_valid(): + _fail_dedicated_server(config.error_message) + return + var data_path: String = config.data_directory + var manifest_path: String = data_path.path_join( + PlayerDataRoot.MANIFEST_FILENAME + ) + if not FileAccess.file_exists(manifest_path): + var created: Dictionary = _data_root.create_unbound_root(data_path) + if not bool(created.get("ok", false)): + _fail_dedicated_server(str(created.get( + "message", "Could not create the server data directory." + ))) + return + if not _data_root.activate_process_root(data_path): + _fail_dedicated_server(_data_root.error_message) + return + var identity_directory: String = data_path.path_join("server") + if DirAccess.make_dir_recursive_absolute(identity_directory) != OK: + _fail_dedicated_server("Could not create the server identity directory.") + return + _host_identity.configure("host_identity", true, identity_directory) + if not _discovery.set_base_url_override(config.discovery_url): + _fail_dedicated_server("The discovery URL is invalid.") + return + if not _network_session.configure_dedicated_operators( + config.operator_fingerprints + ): + _fail_dedicated_server("The server operator list is invalid.") + return + _configure_portable_stores() + if not _discovery.configure_dedicated_runtime(config.server_name): + _fail_dedicated_server("The server room name is invalid.") + return + _initialize_application(true) + _player.set_local_control(false) + _player.visible = false + _player.collision_layer = 0 + _player.collision_mask = 0 + _player.set_physics_process(false) + if not _network_session.start_dedicated_host( + config.port, + config.max_players, + config.bind_address, + ): + _fail_dedicated_server("Could not start the dedicated server.") + return + _network_session.set_session_display_name(config.server_name) + if not _network_session.set_host_open(true): + _fail_dedicated_server("Could not open the dedicated server.") + return + _player_jobs.begin_progression_session() + if config.public_listing and not _discovery.set_discoverable(true): + _network_session.disconnect_session("Discovery setup failed.") + _fail_dedicated_server(_discovery.get_host_status_message()) + return + print( + "NETfishing dedicated server ready: %s on %s:%d (%d players, %s)" + % [ + config.server_name, + config.bind_address, + config.port, + config.max_players, + "public" if config.public_listing else "unlisted", + ] + ) + print( + "Server identity: %s" + % _network_session.get_host_identity_fingerprint() + ) + + +func _fail_dedicated_server(message: String) -> void: + push_error("Dedicated server startup failed: %s" % message) + get_tree().quit(1) + + func _configure_portable_stores() -> void: _save_manager.configure_storage( _data_root.path_for(&"player_save"), _data_root @@ -265,6 +388,10 @@ func _configure_portable_stores() -> void: func _initialize_after_data_root() -> void: + _initialize_application(false) + + +func _initialize_application(dedicated: bool) -> void: if _application_initialized: return _application_initialized = true @@ -284,16 +411,36 @@ func _initialize_after_data_root() -> void: _known_players, _server_trust, _host_bans, + dedicated, ) - _world_time_visuals.setup( - _world_time, - _test_world.get_world_environment(), - _test_world.get_sun(), - _world_weather, - _player, - ) + _discovery.setup(_network_session) + if not dedicated: + _world_time_visuals.setup( + _world_time, + _test_world.get_world_environment(), + _test_world.get_sun(), + _world_weather, + _player, + Callable(_player, "get_active_gameplay_camera"), + ) + if not _world_time.natural_time_advanced.is_connected( + _on_natural_time_advanced + ): + _world_time.natural_time_advanced.connect( + _on_natural_time_advanced + ) _network_world_time.setup(_network_session, _world_time) _network_world_weather.setup(_network_session, _world_weather) + _player_jobs.setup( + _player.wallet, + _player.experience, + _player.collection_log, + fish_catalog, + _world_time, + _world_weather, + _network_session, + ) + _network_jobs.setup(_network_session, _player_jobs) _identity_backups.setup(_data_root, _player_identity, _host_identity) _network_profile_service.setup( _network_session, @@ -310,22 +457,23 @@ func _initialize_after_data_root() -> void: _network_chat, _network_mail, ) - _network_session.set_local_appearance_snapshot( - _appearance_store.get_snapshot() - ) - _network_session.join_authenticated.connect( - _on_network_join_authenticated - ) - _network_session.connection_error.connect( - _on_network_connection_error - ) - _network_session.server_trust_required.connect( - _on_server_trust_required - ) + if not dedicated: + _network_session.set_local_appearance_snapshot( + _appearance_store.get_snapshot() + ) + _network_session.join_authenticated.connect( + _on_network_join_authenticated + ) + _network_session.connection_error.connect( + _on_network_connection_error + ) + _network_session.server_trust_required.connect( + _on_server_trust_required + ) + _network_session.server_lost.connect(_on_network_server_lost) _network_session.peer_identity_observed.connect( _on_peer_identity_observed ) - _network_session.server_lost.connect(_on_network_server_lost) _network_session.remote_recovery_requested.connect( _on_remote_recovery_requested ) @@ -342,10 +490,14 @@ func _initialize_after_data_root() -> void: _player.bag.setup(item_catalog) _player.hotbar.setup(_player.bag, item_catalog, _player.inventory) _shop_interaction = _test_world.get_fishing_shop() - _shop_interaction.setup_local_player(_player) - _shop_interaction.local_player_range_changed.connect( - _on_shop_range_changed - ) + if not dedicated: + _shop_interaction.setup_local_player(_player) + _shop_interaction.local_player_range_changed.connect( + _on_shop_range_changed + ) + _game_ui.set_shop_npc_player_in_range( + _shop_interaction.is_local_player_in_range() + ) _save_manager.setup( _player.inventory, _player.collection_log, @@ -359,7 +511,10 @@ func _initialize_after_data_root() -> void: _player.art_unlocks, _player.experience, _world_time, + _world_weather, + _player_jobs, ) + _player_jobs.set_save_manager(_save_manager) _save_manager.set_autosave_enabled(false) _asset_reservations.setup( _player.wallet, _player.inventory, _player.bag, item_catalog @@ -400,17 +555,19 @@ func _initialize_after_data_root() -> void: _player, _surface_drawings_root, _player.bag, + _player.hotbar, _player.art_unlocks, ) _network_player_list.set_surface_drawing_service( _network_surface_drawing ) - _network_chat.setup(_network_session) + _network_chat.setup(_network_session, _world_time, _world_weather) _network_fishing.setup( _network_session, _player_spawn_service, _fishing_spot, _player.inventory, + _player.bag, _player.collection_log, _player.cooler_capacity, _player.experience, @@ -436,6 +593,7 @@ func _initialize_after_data_root() -> void: sale_buyers, _asset_reservations ) + _player_jobs.bind_authoritative_services(_network_fishing, _network_sale) _network_shop.setup( _network_session, _player_spawn_service, @@ -467,6 +625,8 @@ func _initialize_after_data_root() -> void: _world_time, _world_weather, ) + if dedicated: + return _game_ui.setup( _player, _player.inventory, @@ -495,11 +655,15 @@ func _initialize_after_data_root() -> void: _asset_reservations, _network_profile_service, _network_player_list, + _discovery, _settings_manager, _network_surface_drawing, _player.art_unlocks, _world_time, _world_weather, + _player_jobs, + _test_world.get_world_environment(), + _test_world.get_sun(), ) _game_ui.setup_data_and_identity( _data_root, @@ -509,6 +673,7 @@ func _initialize_after_data_root() -> void: _network_session, _interface_fonts, ) + _game_ui.setup_controller_mapping(_controller_mapping_manager) _data_root.conflict_detected.connect(_on_portable_conflict) _data_root.status_changed.connect(_on_data_root_status) if ( @@ -568,6 +733,7 @@ func _initialize_after_data_root() -> void: _network_session, _saved_servers, _server_trust, + _discovery, ) var pause_menu: PauseMenuType = _game_ui.get_pause_menu() pause_menu.setup( @@ -578,6 +744,7 @@ func _initialize_after_data_root() -> void: _network_session, _saved_servers, _server_trust, + _discovery, ) title_screen.join_game_requested.connect(_on_title_join_game_requested) pause_menu.join_game_requested.connect(_on_pause_join_game_requested) @@ -591,16 +758,13 @@ func _initialize_after_data_root() -> void: pause_menu.menu_visibility_changed.connect( _game_ui.set_system_menu_open ) - _player.hotbar.selected_slot_changed.connect( + _player.hotbar.selected_assignment_changed.connect( _on_active_hotbar_item_changed ) _player.bag.contents_changed.connect(_refresh_active_hotbar_item) _fishing_spot.ready_for_equipment_refresh.connect( _refresh_active_hotbar_item ) - _fishing_spot.fish_showcase_toggle_requested.connect( - _network_fish_showcase.toggle_selected_fish - ) _water_recovery.recovery_starting.connect( _on_water_recovery_starting ) @@ -610,10 +774,7 @@ func _initialize_after_data_root() -> void: _water_recovery.local_respawn_completed.connect( _on_local_respawn_completed ) - _on_active_hotbar_item_changed( - _player.hotbar.get_selected_slot(), - _player.hotbar.get_selected_item_id() - ) + _refresh_active_hotbar_item() _show_title_music(true) @@ -625,10 +786,10 @@ func _show_data_root_setup() -> void: _data_setup_dialog.cancel_button_text = "Quit" _data_setup_dialog.confirmed.connect(_use_default_data_root) _data_setup_dialog.canceled.connect(get_tree().quit) - _data_setup_dialog.add_button( + _data_setup_choose_button = _data_setup_dialog.add_button( "Choose Another Folder", false, "choose" ) - _data_setup_dialog.add_button( + _data_setup_current_button = _data_setup_dialog.add_button( "Keep Current Location", false, "current" ) _data_setup_dialog.custom_action.connect(_on_data_setup_action) @@ -643,20 +804,54 @@ func _show_data_root_setup() -> void: % (default_path if not default_path.is_empty() else "Choose a folder") ) _data_setup_dialog.ok_button_text = ( - "Move to Documents/NETFISHING" if legacy else "Use This Folder" + ( + "Move to Documents/NETFISHING" + if legacy else "Use This Folder" + ) + if not default_path.is_empty() + else "Keep Current Location" ) + _data_setup_current_button.visible = not default_path.is_empty() if not _data_root.error_message.is_empty(): _data_setup_dialog.dialog_text = ( "Your NETfishing data folder is unavailable.\n\n%s" % _data_root.error_message ) _data_setup_dialog.popup_centered(Vector2i(640, 360)) + _focus_data_setup_actions.call_deferred() + + +func _focus_data_setup_actions() -> void: + if _data_setup_dialog == null or not _data_setup_dialog.visible: + return + var actions: Array[Button] = [ + _data_setup_dialog.get_ok_button(), + _data_setup_choose_button, + ] + if _data_setup_current_button.visible: + actions.append(_data_setup_current_button) + actions.append(_data_setup_dialog.get_cancel_button()) + for index: int in actions.size(): + var action: Button = actions[index] + var previous: Button = actions[posmod(index - 1, actions.size())] + var next: Button = actions[(index + 1) % actions.size()] + action.focus_mode = Control.FOCUS_ALL + action.focus_neighbor_left = action.get_path_to(previous) + action.focus_neighbor_top = action.focus_neighbor_left + action.focus_neighbor_right = action.get_path_to(next) + action.focus_neighbor_bottom = action.focus_neighbor_right + _data_setup_dialog.get_ok_button().grab_focus() func _use_default_data_root() -> void: var path: String = _data_root.default_visible_path() if path.is_empty(): - _show_folder_picker() + _activate_selected_data_path( + ProjectSettings.globalize_path( + PlayerDataRoot.APP_DATA_PORTABLE_PATH + ), + true, + ) return _activate_selected_data_path(path) @@ -705,7 +900,7 @@ func _open_folder_picker_after_modal(generation: int) -> void: if not _data_root.default_visible_path().is_empty() else OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) ) - _data_folder_dialog.popup_centered_ratio(0.75) + _interface_fonts.popup_file_dialog(_data_folder_dialog) func _on_data_folder_picker_canceled() -> void: @@ -884,6 +1079,8 @@ func _confirm_server_trust() -> void: _server_trust_dialog.ok_button_text = "Replace Pin" _server_trust_dialog.popup_centered(Vector2i(520, 260)) return + if _server_trust_dialog != null: + _server_trust_dialog.hide() _network_session.resolve_server_trust(true) @@ -902,11 +1099,20 @@ func _on_peer_identity_observed(_peer_id: int, status: String) -> void: func _input(event: InputEvent) -> void: + if _handle_data_root_controller_input(event): + get_viewport().set_input_as_handled() + return + if _game_ui.is_controller_mapping_capturing(): + return if ( - not event.is_action_pressed("ui_cancel") + not ( + event.is_action_pressed("ui_cancel") + or event.is_action_pressed("open_system_menu") + ) or (event is InputEventKey and event.echo) ): return + var pause_open_requested: bool = _is_pause_open_request(event) var title_screen: TitleScreenType = _game_ui.get_title_screen() if title_screen.visible or not _gameplay_started: return @@ -925,7 +1131,8 @@ func _input(event: InputEvent) -> void: get_viewport().set_input_as_handled() return if ( - not _water_recovery.is_recovery_active() + pause_open_requested + and not _water_recovery.is_recovery_active() and _fishing_spot.can_open_system_menu() ): _game_ui.close_player_menu_for_game_menu() @@ -933,6 +1140,71 @@ func _input(event: InputEvent) -> void: get_viewport().set_input_as_handled() +func _handle_data_root_controller_input(event: InputEvent) -> bool: + var button_event := event as InputEventJoypadButton + if button_event == null or not button_event.pressed: + return false + var setup_visible: bool = ( + _data_setup_dialog != null and _data_setup_dialog.visible + ) + var picker_visible: bool = ( + _data_folder_dialog != null and _data_folder_dialog.visible + ) + if not setup_visible and not picker_visible: + return false + var use_mapping: bool = _controller_mapping_manager != null + var accept_pressed: bool = ( + _controller_mapping_manager.event_matches_role( + event, + ControllerMappingManagerType.ROLE_A, + ) + if use_mapping + else ( + button_event.button_index == JOY_BUTTON_A + or event.is_action_pressed("ui_accept") + ) + ) + var cancel_pressed: bool = ( + _controller_mapping_manager.event_matches_role( + event, + ControllerMappingManagerType.ROLE_B, + ) + if use_mapping + else ( + button_event.button_index == JOY_BUTTON_B + or event.is_action_pressed("ui_cancel") + ) + ) + if cancel_pressed: + if picker_visible: + _on_data_folder_picker_canceled() + else: + _data_setup_dialog.get_cancel_button().pressed.emit() + return true + if not accept_pressed: + return false + var focused: Control = ( + _data_folder_dialog.gui_get_focus_owner() + if picker_visible + else _data_setup_dialog.gui_get_focus_owner() + ) + var focused_button := focused as BaseButton + if focused_button != null and not focused_button.disabled: + focused_button.pressed.emit() + return true + if setup_visible: + _data_setup_dialog.get_ok_button().pressed.emit() + return true + return false + + +func _is_pause_open_request(event: InputEvent) -> bool: + return ( + event.is_action_pressed("open_system_menu") + or event is InputEventKey + ) + + func _unhandled_input(event: InputEvent) -> void: if ( not _gameplay_started @@ -948,17 +1220,41 @@ func _unhandled_input(event: InputEvent) -> void: ): _game_ui.set_shop_prompt_visible(false) get_viewport().set_input_as_handled() + elif event is InputEventJoypadButton and _player != null: + _player.hotbar.clear_slot(_player.hotbar.get_selected_slot()) + get_viewport().set_input_as_handled() func _process(_delta: float) -> void: - _game_ui.set_shop_prompt_visible(_can_show_shop_prompt()) + if _dedicated_runtime: + return + var show_shop_prompt := _can_show_shop_prompt() + var shop_prompt_anchor := ( + _shop_interaction.get_prompt_anchor_position() + if _shop_interaction != null + else Vector3.ZERO + ) + _game_ui.set_shop_prompt_visible( + show_shop_prompt, + shop_prompt_anchor, + ) func _apply_runtime_settings(settings: PlayerSettingsType) -> void: if settings == null: return + var requested_window_mode: DisplayServer.WindowMode = ( + DisplayServer.WINDOW_MODE_FULLSCREEN + if settings.fullscreen_enabled + else DisplayServer.WINDOW_MODE_WINDOWED + ) + if DisplayServer.window_get_mode() != requested_window_mode: + DisplayServer.window_set_mode(requested_window_mode) _apply_world_pixelation(settings.world_pixel_size) _ui_pixelation.set_pixel_size(settings.ui_pixel_size) + _ui_pixelation.set_on_screen_keyboard_enabled( + settings.on_screen_keyboard_enabled + ) _game_ui.get_title_screen().set_world_pixelation( settings.world_pixel_size ) @@ -974,13 +1270,18 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void: _game_ui.set_edge_docks( settings.chat_dock_right, settings.paint_dock_right, + settings.chat_mobile_mode, ) func _apply_world_pixelation(pixel_size: int) -> void: var root_viewport: Viewport = get_viewport() root_viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_NEAREST - root_viewport.scaling_3d_scale = 1.0 + root_viewport.scaling_3d_scale = ( + 0.75 + if OS.get_environment("NETFISHING_LOW_END") == "1" + else 1.0 + ) root_viewport.msaa_3d = Viewport.MSAA_DISABLED root_viewport.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED root_viewport.use_taa = false @@ -1003,7 +1304,18 @@ func _reset_pixelation() -> void: func _set_gameplay_active(active: bool) -> void: + var was_gameplay_started: bool = _gameplay_started _gameplay_started = active + _shoreline_ambience.set_active(active) + _rain_ambience.set_active(active) + if active and not was_gameplay_started: + var current_hour: float = _world_time.get_time_hours() + _dusk_music_played_for_natural_day = ( + current_hour >= WorldTimeServiceType.DUSK_START_HOUR + or current_hour < WorldTimeServiceType.DAWN_START_HOUR + ) + elif not active: + _dusk_music.stop() _title_background.visible = not active _world_pixelation.set_gameplay_active(active) _ui_pixelation.set_gameplay_active(active) @@ -1017,17 +1329,63 @@ func _set_gameplay_active(active: bool) -> void: _game_ui.set_gameplay_ui_enabled(active) _save_manager.set_autosave_enabled(active) if active: + _player_jobs.begin_progression_session() _refresh_active_hotbar_item() else: + _player_jobs.end_progression_session() _set_player_menu_backdrop_visible(false) _set_shop_backdrop_visible(false) -func _set_player_menu_backdrop_visible(is_visible: bool) -> void: +func _on_natural_time_advanced(advanced_hours: float) -> void: + if not _gameplay_started or advanced_hours <= 0.0: + return + var current_hour: float = _world_time.get_time_hours() + var previous_hour: float = fposmod( + current_hour - advanced_hours, + WorldTimeServiceType.HOURS_PER_DAY, + ) + if _natural_interval_crosses_hour( + previous_hour, + advanced_hours, + WorldTimeServiceType.DAWN_START_HOUR, + ): + _dusk_music_played_for_natural_day = false + if ( + not _dusk_music_played_for_natural_day + and _natural_interval_crosses_hour( + previous_hour, + advanced_hours, + WorldTimeServiceType.DUSK_START_HOUR, + ) + ): + _dusk_music_played_for_natural_day = true + _dusk_music.play(0.0) + + +static func _natural_interval_crosses_hour( + previous_hour: float, + advanced_hours: float, + target_hour: float, +) -> bool: + if advanced_hours >= WorldTimeServiceType.HOURS_PER_DAY: + return true + var distance_to_target: float = fposmod( + target_hour - previous_hour, + WorldTimeServiceType.HOURS_PER_DAY, + ) + return ( + distance_to_target > 0.0 + and distance_to_target + <= advanced_hours + TIME_CROSSING_EPSILON_HOURS + ) + + +func _set_player_menu_backdrop_visible(requested_visible: bool) -> void: if _player_menu_backdrop_tween != null: _player_menu_backdrop_tween.kill() _player_menu_backdrop_tween = null - var should_show: bool = is_visible and _gameplay_started + var should_show: bool = requested_visible and _gameplay_started if should_show: var was_visible: bool = _player_menu_backdrop.visible _player_menu_backdrop.visible = true @@ -1058,11 +1416,11 @@ func _set_player_menu_backdrop_visible(is_visible: bool) -> void: ) -func _set_shop_backdrop_visible(is_visible: bool) -> void: +func _set_shop_backdrop_visible(requested_visible: bool) -> void: if _shop_backdrop_tween != null: _shop_backdrop_tween.kill() _shop_backdrop_tween = null - var should_show: bool = is_visible and _gameplay_started + var should_show: bool = requested_visible and _gameplay_started if should_show: var was_visible: bool = _shop_backdrop.visible _shop_backdrop.visible = true @@ -1141,7 +1499,10 @@ func _prepare_private_host() -> bool: NetworkSessionType.State.SERVER_LOST, ]: _network_session.reset_failure() - if not _network_session.start_private_host(): + if not _network_session.start_private_host( + NetworkSessionType.DEFAULT_PORT, + NetworkSessionType.DEFAULT_PRIVATE_HOST_PORT_ATTEMPTS, + ): _game_ui.get_title_screen().report_network_error( "Could not start the private multiplayer session." ) @@ -1195,8 +1556,13 @@ func _on_pause_join_game_requested(endpoint: String) -> void: _join_requested_from_title = false _pending_join_endpoint = endpoint _game_ui.get_pause_menu().close_for_title_transition() + var preserved_public_join: bool = ( + _discovery.preserve_public_join_for_session_switch() + ) _network_session.disconnect_session("Connecting to another game.") if not _network_session.join_direct(endpoint): + if preserved_public_join: + _discovery.cancel_pending_public_join() _handle_failed_session_switch( "Could not begin the direct connection." ) @@ -1281,14 +1647,16 @@ func _handle_failed_session_switch(message: String) -> void: _show_title_music(true) -func _on_network_server_lost() -> void: +func _on_network_server_lost(message: String) -> void: if not _gameplay_started: return + _join_requested_from_title = false + _join_requested_from_pause = false + _pending_join_endpoint = "" _set_gameplay_active(false) var title_screen: TitleScreenType = _game_ui.get_title_screen() - title_screen.reopen() - title_screen.open_join_game_page(_pending_join_endpoint) - title_screen.report_network_error("The server connection was lost.") + title_screen.reopen_to_menu() + title_screen.report_network_error(message) _show_title_music(true) @@ -1383,23 +1751,33 @@ func _on_remote_recovery_presentation_changed( func _on_active_hotbar_item_changed( _slot_index: int, - item_id: StringName, + kind: int, + identity: StringName, ) -> void: if _fishing_spot.state != FishingSpotType.FishingState.READY: return + var item_id: StringName = ( + identity + if kind == PlayerHotbarType.AssignmentKind.ITEM + else StringName() + ) var item = item_catalog.get_item_by_id(item_id) var active_is_rod: bool = ( item != null and item.category == ItemDataType.Category.ROD and _player.bag.owns_item(item_id) ) - _player.set_active_item_is_rod(active_is_rod) - _player.set_active_art_kit( - item.icon if item != null else null, + var active_is_art_kit: bool = ( item_id == ArtShopStockType.ART_KIT_ITEM_ID and item != null - and _player.bag.owns_item(item_id), + and _player.bag.owns_item(item_id) ) + _player.set_active_item_is_rod(active_is_rod, true) + _player.set_active_art_kit( + item.icon if item != null else null, + active_is_art_kit, + ) + _game_ui.set_surface_drawing_hotbar_selected(active_is_art_kit) _network_item_use.submit_local_equipped(item_id, active_is_rod or ( item != null and _player.bag.owns_item(item_id) )) @@ -1407,9 +1785,18 @@ func _on_active_hotbar_item_changed( func _refresh_active_hotbar_item() -> void: + var kind: int = ( + _player.hotbar.get_selected_assignment_kind() + ) + var identity: StringName = StringName() + if kind == PlayerHotbarType.AssignmentKind.ITEM: + identity = _player.hotbar.get_selected_item_id() + elif kind == PlayerHotbarType.AssignmentKind.FISH: + identity = _player.hotbar.get_selected_fish_catch_id() _on_active_hotbar_item_changed( _player.hotbar.get_selected_slot(), - _player.hotbar.get_selected_item_id() + kind, + identity, ) @@ -1493,15 +1880,21 @@ func _finish_quit() -> void: func _exit_tree() -> void: + if _dedicated_runtime and _network_session != null: + _network_session.disconnect_session("Dedicated server stopping.") if _title_music_tween != null: _title_music_tween.kill() _title_music_tween = null if is_instance_valid(_title_music): _title_music.stop() _title_music.stream = null + if is_instance_valid(_dusk_music): + _dusk_music.stop() + _dusk_music.stream = null func _on_shop_range_changed(in_range: bool) -> void: + _game_ui.set_shop_npc_player_in_range(in_range) if not in_range: _game_ui.get_fishing_shop().close_for_range_exit() _game_ui.set_shop_prompt_visible(_can_show_shop_prompt()) diff --git a/main/main.tscn b/main/main.tscn index 1e60b8d..6b9f496 100644 --- a/main/main.tscn +++ b/main/main.tscn @@ -43,14 +43,20 @@ [ext_resource type="Texture2D" uid="uid://f8a0cs0s7ler" path="res://art/patterns/pattern_tealdot.png" id="41_menu_pattern"] [ext_resource type="Shader" uid="uid://byw3iub66qvog" path="res://ui/player_menu_pattern.gdshader" id="42_menu_pattern"] [ext_resource type="Texture2D" uid="uid://b6xws1d2dnbnn" path="res://art/patterns/pattern_moneyfish.png" id="43_shop_pattern"] -[ext_resource type="PackedScene" uid="uid://w7n4gjqq1juc" path="res://art/exported/characters/base/netfishing_base_character.glb" id="44_4pcu1"] -[ext_resource type="Script" path="res://network/network_fish_showcase_service.gd" id="45_fish_showcase"] -[ext_resource type="Script" path="res://network/network_surface_drawing_service.gd" id="46_surface_drawing"] -[ext_resource type="Script" path="res://world/world_time_service.gd" id="47_world_time"] -[ext_resource type="Script" path="res://world/world_time_visual_controller.gd" id="48_world_time_visuals"] -[ext_resource type="Script" path="res://network/network_world_time_service.gd" id="49_network_world_time"] -[ext_resource type="Script" path="res://world/world_weather_service.gd" id="50_world_weather"] -[ext_resource type="Script" path="res://network/network_world_weather_service.gd" id="51_network_world_weather"] +[ext_resource type="Script" uid="uid://m4n0bn38f34i" path="res://network/network_fish_showcase_service.gd" id="45_fish_showcase"] +[ext_resource type="Script" uid="uid://bkiepgu8j5r6t" path="res://network/network_surface_drawing_service.gd" id="46_surface_drawing"] +[ext_resource type="Script" uid="uid://5lceyulaglpf" path="res://world/world_time_service.gd" id="47_world_time"] +[ext_resource type="Script" uid="uid://svirs8vbypad" path="res://world/world_time_visual_controller.gd" id="48_world_time_visuals"] +[ext_resource type="Script" uid="uid://blig76aes25j" path="res://network/network_world_time_service.gd" id="49_network_world_time"] +[ext_resource type="Script" uid="uid://v4c54isq6w74" path="res://world/world_weather_service.gd" id="50_world_weather"] +[ext_resource type="Script" uid="uid://llj46fx0oco7" path="res://network/network_world_weather_service.gd" id="51_network_world_weather"] +[ext_resource type="Script" uid="uid://daqtje84gqgu0" path="res://jobs/player_job_service.gd" id="52_player_jobs"] +[ext_resource type="Script" uid="uid://drlyytr2o3uw8" path="res://network/network_job_service.gd" id="53_network_jobs"] +[ext_resource type="Script" uid="uid://b0c3o2vy76fg3" path="res://world/shoreline_ambience.gd" id="54_shoreline_ambience"] +[ext_resource type="AudioStream" uid="uid://dck3tf8qkadea" path="res://audio/ambience/waves.wav" id="55_waves"] +[ext_resource type="Script" uid="uid://c80x8jkdnx0xy" path="res://settings/controller_mapping_manager.gd" id="56_controller_mapping"] +[ext_resource type="Script" uid="uid://d0jv8n2nfqia0" path="res://network/discovery_client.gd" id="57_discovery"] +[ext_resource type="AudioStream" uid="uid://c4y8puv0n6xk8" path="res://audio/music/world/craft.mp3" id="dusk_music"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"] shader = ExtResource("40_title_water") @@ -79,6 +85,57 @@ shader_parameter/source_tile_size = Vector2(64, 64) shader_parameter/display_scale = 1.75 shader_parameter/scroll_velocity_pixels = Vector2(-7, -5) +[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_hifdc"] +data = PackedVector3Array(-13.598646, -21.527378, 3.4402227, -13.598646, -21.04931, 3.716229, -13.64679, -21.345833, 3.1714065, -13.64679, -21.345833, 3.1714065, -13.598646, -21.04931, 3.716229, -13.598646, -21.04931, 3.1642165, -13.598646, -21.601322, 3.716229, -13.598646, -21.04931, 3.716229, -13.598646, -21.527378, 3.4402227, -13.598646, -21.04931, 3.1642165, -13.598646, -21.04931, 3.716229, -13.598646, -20.773304, 3.238161, -13.604641, -21.54901, 3.9200878, -13.598646, -21.04931, 3.716229, -13.598646, -21.601322, 3.716229, -13.598646, -20.773304, 3.238161, -13.598646, -21.04931, 3.716229, -13.598646, -20.571243, 3.4402227, -13.598646, -21.325317, 4.194297, -13.598646, -21.04931, 3.716229, -13.604641, -21.54901, 3.9200878, -13.598646, -20.571243, 3.4402227, -13.598646, -21.04931, 3.716229, -13.598646, -20.4973, 3.716229, -13.602205, -21.078318, 4.268241, -13.598646, -21.04931, 3.716229, -13.598646, -21.325317, 4.194297, -13.598646, -20.4973, 3.716229, -13.598646, -21.04931, 3.716229, -13.598646, -20.571243, 3.992235, -13.598646, -20.773304, 4.194297, -13.598646, -21.04931, 3.716229, -13.602205, -21.078318, 4.268241, -13.598646, -20.571243, 3.992235, -13.598646, -21.04931, 3.716229, -13.598646, -20.773304, 4.194297, -19.042458, -21.325317, 3.2381601, -19.042458, -21.04931, 3.716228, -19.042458, -21.527378, 3.4402218, -19.042458, -21.527378, 3.4402218, -19.042458, -21.04931, 3.716228, -19.042458, -21.601322, 3.716228, -19.042458, -21.04931, 3.1642156, -19.042458, -21.04931, 3.716228, -19.042458, -21.325317, 3.2381601, -19.042458, -21.601322, 3.716228, -19.042458, -21.04931, 3.716228, -19.042458, -21.527378, 3.992234, -19.042458, -20.773304, 3.2381601, -19.042458, -21.04931, 3.716228, -19.042458, -21.04931, 3.1642156, -19.042458, -21.527378, 3.992234, -19.042458, -21.04931, 3.716228, -19.042458, -21.325317, 4.194296, -18.784327, -20.60149, 3.4511306, -19.042458, -21.04931, 3.716228, -19.042458, -20.773304, 3.2381601, -19.042458, -21.325317, 4.194296, -19.042458, -21.04931, 3.716228, -19.042458, -21.04931, 4.26824, -19.042458, -20.4973, 3.716228, -19.042458, -21.04931, 3.716228, -18.784327, -20.60149, 3.4511306, -19.042458, -21.04931, 4.26824, -19.042458, -21.04931, 3.716228, -19.042458, -20.773304, 4.194296, -19.042458, -20.571243, 3.992234, -19.042458, -21.04931, 3.716228, -19.042458, -20.4973, 3.716228, -19.042458, -20.773304, 4.194296, -19.042458, -21.04931, 3.716228, -19.042458, -20.571243, 3.992234, -14.534702, -21.32513, 3.238533, -17.467253, -21.04931, 3.1645257, -17.467253, -21.32513, 3.2384703, -17.467253, -21.32513, 3.2384703, -19.084044, -21.586075, 3.4063175, -17.467253, -21.52707, 3.440408, -17.467253, -21.32513, 3.2384703, -19.084044, -21.35922, 3.1794631, -19.084044, -21.586075, 3.4063175, -17.467253, -21.04931, 3.1645257, -19.084044, -21.35922, 3.1794631, -17.467253, -21.32513, 3.2384703, -17.467253, -21.32513, 3.2384703, -17.467253, -21.52707, 3.440408, -14.516907, -21.527006, 3.4404085, -17.467253, -21.32513, 3.2384703, -14.516907, -21.527006, 3.4404085, -14.534702, -21.32513, 3.238533, -17.467253, -21.04931, 3.1645257, -19.084044, -21.04931, 3.0964072, -19.084044, -21.35922, 3.1794631, -13.605204, -21.379738, 3.1126475, -14.534702, -21.32513, 3.238533, -14.516907, -21.527006, 3.4404085, -13.605204, -21.379738, 3.1126475, -14.516907, -21.527006, 3.4404085, -13.55706, -21.586075, 3.4063187, -13.55706, -21.04931, 3.0964081, -14.534702, -21.32513, 3.238533, -13.605204, -21.379738, 3.1126475, -13.55706, -21.04931, 3.0964081, -14.519905, -21.04931, 3.1645882, -14.534702, -21.32513, 3.238533, -14.534702, -21.32513, 3.238533, -14.519905, -21.04931, 3.1645882, -17.467253, -21.04931, 3.1645257, -13.55706, -20.7394, 3.179464, -14.519905, -21.04931, 3.1645882, -13.55706, -21.04931, 3.0964081, -17.467253, -20.77349, 3.2384703, -19.084044, -21.04931, 3.0964072, -17.467253, -21.04931, 3.1645257, -17.467253, -20.77349, 3.2384703, -17.467253, -21.04931, 3.1645257, -14.519905, -21.04931, 3.1645882, -17.467253, -20.77349, 3.2384703, -19.084044, -20.7394, 3.1794631, -19.084044, -21.04931, 3.0964072, -17.467253, -20.571552, 3.440408, -19.084044, -20.7394, 3.1794631, -17.467253, -20.77349, 3.2384703, -17.467253, -20.571552, 3.440408, -18.82591, -20.542793, 3.4172263, -19.084044, -20.7394, 3.1794631, -17.467253, -20.77349, 3.2384703, -14.519905, -21.04931, 3.1645882, -14.509039, -20.77349, 3.238533, -17.467253, -20.571552, 3.440408, -17.467253, -20.77349, 3.2384703, -14.509039, -20.77349, 3.238533, -13.55706, -20.7394, 3.179464, -14.509039, -20.77349, 3.238533, -14.519905, -21.04931, 3.1645882, -17.467253, -20.571552, 3.440408, -14.509039, -20.77349, 3.238533, -14.509039, -20.571615, 3.4404085, -13.55706, -20.512547, 3.4063187, -14.509039, -20.77349, 3.238533, -13.55706, -20.7394, 3.179464, -13.55706, -20.512547, 3.4063187, -14.509039, -20.571615, 3.4404085, -14.509039, -20.77349, 3.238533, -17.467253, -21.52707, 3.440408, -14.509039, -21.60095, 3.7162287, -14.516907, -21.527006, 3.4404085, -13.55706, -21.586075, 3.4063187, -14.516907, -21.527006, 3.4404085, -14.509039, -21.60095, 3.7162287, -13.55706, -21.586075, 3.4063187, -14.509039, -21.60095, 3.7162287, -13.55706, -21.669132, 3.716229, -13.55706, -21.669132, 3.716229, -14.509039, -21.60095, 3.7162287, -14.511475, -21.527006, 3.992049, -13.55706, -21.669132, 3.716229, -14.511475, -21.527006, 3.992049, -13.563242, -21.607708, 3.9539921, -17.467253, -21.601013, 3.7162282, -14.511475, -21.527006, 3.992049, -14.509039, -21.60095, 3.7162287, -17.467253, -21.52707, 3.440408, -17.467253, -21.601013, 3.7162282, -14.509039, -21.60095, 3.7162287, -17.467253, -21.601013, 3.7162282, -17.467253, -21.52707, 3.9920485, -14.511475, -21.527006, 3.992049, -17.467253, -21.601013, 3.7162282, -19.084044, -21.586075, 4.0261383, -17.467253, -21.52707, 3.9920485, -17.467253, -21.601013, 3.7162282, -19.084044, -21.669132, 3.716228, -19.084044, -21.586075, 4.0261383, -17.467253, -21.52707, 3.440408, -19.084044, -21.669132, 3.716228, -17.467253, -21.601013, 3.7162282, -17.467253, -21.52707, 3.440408, -19.084044, -21.586075, 3.4063175, -19.084044, -21.669132, 3.716228, -17.467253, -21.52707, 3.9920485, -14.509039, -21.32513, 4.1939244, -14.511475, -21.527006, 3.992049, -13.563242, -21.607708, 3.9539921, -14.511475, -21.527006, 3.992049, -14.509039, -21.32513, 4.1939244, -13.563242, -21.607708, 3.9539921, -14.509039, -21.32513, 4.1939244, -13.557062, -21.35922, 4.2529936, -13.557062, -21.35922, 4.2529936, -14.509039, -21.32513, 4.1939244, -14.510912, -21.04931, 4.267869, -13.557062, -21.35922, 4.2529936, -14.510912, -21.04931, 4.267869, -13.560621, -21.078318, 4.3360496, -17.467253, -21.52707, 3.9920485, -17.467253, -21.32513, 4.193986, -14.509039, -21.32513, 4.1939244, -17.467253, -21.32513, 4.193986, -14.510912, -21.04931, 4.267869, -14.509039, -21.32513, 4.1939244, -17.467253, -21.52707, 3.9920485, -19.084044, -21.35922, 4.2529926, -17.467253, -21.32513, 4.193986, -17.467253, -21.52707, 3.9920485, -19.084044, -21.586075, 4.0261383, -19.084044, -21.35922, 4.2529926, -17.467253, -21.32513, 4.193986, -19.084044, -21.35922, 4.2529926, -19.084044, -21.04931, 4.3360486, -17.467253, -21.32513, 4.193986, -19.084044, -21.04931, 4.3360486, -17.467253, -21.04931, 4.2679305, -17.467253, -21.32513, 4.193986, -17.467253, -21.04931, 4.2679305, -14.510912, -21.04931, 4.267869, -17.467253, -21.04931, 4.2679305, -19.084044, -21.04931, 4.3360486, -19.084044, -20.7394, 4.2529926, -13.560621, -21.078318, 4.3360496, -14.510912, -21.04931, 4.267869, -14.510912, -20.795122, 4.1217775, -17.467253, -21.04931, 4.2679305, -14.510912, -20.795122, 4.1217775, -14.510912, -21.04931, 4.267869, -13.560621, -21.078318, 4.3360496, -14.510912, -20.795122, 4.1217775, -13.557062, -20.7394, 4.2529936, -17.467253, -21.04931, 4.2679305, -19.084044, -20.7394, 4.2529926, -17.467253, -20.77349, 4.193986, -17.467253, -21.04931, 4.2679305, -17.467253, -20.77349, 4.193986, -14.510912, -20.795122, 4.1217775, -17.467253, -20.77349, 4.193986, -19.084044, -20.7394, 4.2529926, -19.084044, -20.512547, 4.0261383, -17.467253, -20.77349, 4.193986, -19.084044, -20.512547, 4.0261383, -17.467253, -20.571552, 3.9920485, -17.467253, -20.77349, 4.193986, -17.467253, -20.571552, 3.9920485, -14.509039, -20.571615, 3.992049, -17.467253, -20.77349, 4.193986, -14.509039, -20.571615, 3.992049, -14.510912, -20.795122, 4.1217775, -13.557062, -20.7394, 4.2529936, -14.510912, -20.795122, 4.1217775, -14.509039, -20.571615, 3.992049, -13.557062, -20.7394, 4.2529936, -14.509039, -20.571615, 3.992049, -13.55706, -20.512547, 4.0261393, -17.467253, -20.571552, 3.9920485, -14.509039, -20.497732, 3.7162287, -14.509039, -20.571615, 3.992049, -13.55706, -20.512547, 4.0261393, -14.509039, -20.571615, 3.992049, -14.509039, -20.497732, 3.7162287, -13.55706, -20.512547, 4.0261393, -14.509039, -20.497732, 3.7162287, -13.55706, -20.42949, 3.716229, -13.55706, -20.42949, 3.716229, -14.509039, -20.497732, 3.7162287, -14.509039, -20.571615, 3.4404085, -13.55706, -20.42949, 3.716229, -14.509039, -20.571615, 3.4404085, -13.55706, -20.512547, 3.4063187, -17.467253, -20.497608, 3.7162282, -14.509039, -20.571615, 3.4404085, -14.509039, -20.497732, 3.7162287, -17.467253, -20.571552, 3.9920485, -17.467253, -20.497608, 3.7162282, -14.509039, -20.497732, 3.7162287, -17.467253, -20.497608, 3.7162282, -17.467253, -20.571552, 3.440408, -14.509039, -20.571615, 3.4404085, -17.467253, -20.497608, 3.7162282, -18.82591, -20.542793, 3.4172263, -17.467253, -20.571552, 3.440408, -17.467253, -20.497608, 3.7162282, -19.084044, -20.42949, 3.716228, -18.82591, -20.542793, 3.4172263, -17.467253, -20.571552, 3.9920485, -19.084044, -20.42949, 3.716228, -17.467253, -20.497608, 3.7162282, -17.467253, -20.571552, 3.9920485, -19.084044, -20.512547, 4.0261383, -19.084044, -20.42949, 3.716228, -19.084044, -21.35922, 3.1794631, -19.084044, -21.586075, 3.4063175, -19.084044, -21.586075, 3.4063175, -19.084044, -21.586075, 3.4063175, -19.084044, -21.586075, 3.4063175, -19.084044, -21.669132, 3.716228, -19.084044, -21.35922, 3.1794631, -19.084044, -21.35922, 3.1794631, -19.084044, -21.586075, 3.4063175, -19.084044, -21.04931, 3.0964072, -19.084044, -21.35922, 3.1794631, -19.084044, -21.35922, 3.1794631, -19.084044, -21.04931, 3.0964072, -19.084044, -21.04931, 3.0964072, -19.084044, -21.35922, 3.1794631, -19.084044, -21.586075, 3.4063175, -19.084044, -21.669132, 3.716228, -19.084044, -21.669132, 3.716228, -19.084044, -21.669132, 3.716228, -19.084044, -21.669132, 3.716228, -19.084044, -21.586075, 4.0261383, -19.084044, -21.669132, 3.716228, -19.084044, -21.586075, 4.0261383, -19.084044, -21.586075, 4.0261383, -19.084044, -21.586075, 4.0261383, -19.084044, -21.586075, 4.0261383, -19.084044, -21.35922, 4.2529926, -19.084044, -21.586075, 4.0261383, -19.084044, -21.35922, 4.2529926, -19.084044, -21.35922, 4.2529926, -19.084044, -21.35922, 4.2529926, -19.084044, -21.35922, 4.2529926, -19.084044, -21.04931, 4.3360486, -19.084044, -21.35922, 4.2529926, -19.084044, -21.04931, 4.3360486, -19.084044, -21.04931, 4.3360486, -19.084044, -21.04931, 4.3360486, -19.084044, -21.04931, 4.3360486, -19.084044, -20.7394, 4.2529926, -19.084044, -21.04931, 4.3360486, -19.084044, -20.7394, 4.2529926, -19.084044, -20.7394, 4.2529926, -19.084044, -20.7394, 4.2529926, -19.084044, -20.7394, 4.2529926, -19.084044, -20.512547, 4.0261383, -19.084044, -20.7394, 4.2529926, -19.084044, -20.512547, 4.0261383, -19.084044, -20.512547, 4.0261383, -19.084044, -20.512547, 4.0261383, -19.084044, -20.512547, 4.0261383, -19.084044, -20.42949, 3.716228, -19.084044, -20.512547, 4.0261383, -19.084044, -20.42949, 3.716228, -19.084044, -20.42949, 3.716228, -19.084044, -20.42949, 3.716228, -19.084044, -20.42949, 3.716228, -18.82591, -20.542793, 3.4172263, -19.084044, -20.42949, 3.716228, -18.82591, -20.542793, 3.4172263, -18.82591, -20.542793, 3.4172263, -18.82591, -20.542793, 3.4172263, -19.084044, -20.7394, 3.1794631, -19.084044, -20.7394, 3.1794631, -18.82591, -20.542793, 3.4172263, -18.82591, -20.542793, 3.4172263, -19.084044, -20.7394, 3.1794631, -19.084044, -20.7394, 3.1794631, -19.084044, -21.04931, 3.0964072, -19.084044, -21.04931, 3.0964072, -19.084044, -20.7394, 3.1794631, -19.084044, -20.7394, 3.1794631, -19.084044, -21.04931, 3.0964072, -13.55706, -21.586075, 3.4063187, -13.605204, -21.345833, 3.1714065, -13.605204, -21.379738, 3.1126475, -13.605204, -21.379738, 3.1126475, -13.605204, -21.345833, 3.1714065, -13.55706, -21.04931, 3.1642165, -13.55706, -21.586075, 3.4063187, -13.55706, -21.527378, 3.4402227, -13.605204, -21.345833, 3.1714065, -13.605204, -21.379738, 3.1126475, -13.55706, -21.04931, 3.1642165, -13.55706, -21.04931, 3.0964081, -13.55706, -21.669132, 3.716229, -13.55706, -21.527378, 3.4402227, -13.55706, -21.586075, 3.4063187, -13.55706, -21.04931, 3.0964081, -13.55706, -21.04931, 3.1642165, -13.55706, -20.773304, 3.238161, -13.55706, -21.669132, 3.716229, -13.55706, -21.601322, 3.716229, -13.55706, -21.527378, 3.4402227, -13.55706, -21.04931, 3.0964081, -13.55706, -20.773304, 3.238161, -13.55706, -20.7394, 3.179464, -13.563242, -21.607708, 3.9539921, -13.55706, -21.601322, 3.716229, -13.55706, -21.669132, 3.716229, -13.55706, -20.7394, 3.179464, -13.55706, -20.773304, 3.238161, -13.55706, -20.571243, 3.4402227, -13.563242, -21.607708, 3.9539921, -13.563242, -21.54901, 3.9200878, -13.55706, -21.601322, 3.716229, -13.55706, -20.7394, 3.179464, -13.55706, -20.571243, 3.4402227, -13.55706, -20.512547, 3.4063187, -13.557062, -21.35922, 4.2529936, -13.563242, -21.54901, 3.9200878, -13.563242, -21.607708, 3.9539921, -13.55706, -20.512547, 3.4063187, -13.55706, -20.571243, 3.4402227, -13.55706, -20.4973, 3.716229, -13.557062, -21.35922, 4.2529936, -13.55706, -21.325317, 4.194297, -13.563242, -21.54901, 3.9200878, -13.55706, -20.512547, 3.4063187, -13.55706, -20.4973, 3.716229, -13.55706, -20.42949, 3.716229, -13.560621, -21.078318, 4.3360496, -13.55706, -21.325317, 4.194297, -13.557062, -21.35922, 4.2529936, -13.55706, -20.42949, 3.716229, -13.55706, -20.4973, 3.716229, -13.55706, -20.571243, 3.9922352, -13.560621, -21.078318, 4.3360496, -13.560621, -21.078318, 4.268241, -13.55706, -21.325317, 4.194297, -13.55706, -20.42949, 3.716229, -13.55706, -20.571243, 3.9922352, -13.55706, -20.512547, 4.0261393, -13.557062, -20.7394, 4.2529936, -13.560621, -21.078318, 4.268241, -13.560621, -21.078318, 4.3360496, -13.55706, -20.512547, 4.0261393, -13.55706, -20.571243, 3.9922352, -13.55706, -20.773304, 4.194297, -13.557062, -20.7394, 4.2529936, -13.55706, -20.773304, 4.194297, -13.560621, -21.078318, 4.268241, -13.55706, -20.512547, 4.0261393, -13.55706, -20.773304, 4.194297, -13.557062, -20.7394, 4.2529936, -19.084044, -21.35922, 3.1794631, -19.084044, -21.527378, 3.4402218, -19.084044, -21.586075, 3.4063175, -19.084044, -21.586075, 3.4063175, -19.084044, -21.527378, 3.4402218, -19.084044, -21.601322, 3.716228, -19.084044, -21.35922, 3.1794631, -19.084044, -21.325317, 3.2381601, -19.084044, -21.527378, 3.4402218, -19.084044, -21.586075, 3.4063175, -19.084044, -21.601322, 3.716228, -19.084044, -21.669132, 3.716228, -19.084044, -21.04931, 3.0964072, -19.084044, -21.325317, 3.2381601, -19.084044, -21.35922, 3.1794631, -19.084044, -21.669132, 3.716228, -19.084044, -21.601322, 3.716228, -19.084044, -21.527378, 3.992234, -19.084044, -21.04931, 3.0964072, -19.084044, -21.04931, 3.1642156, -19.084044, -21.325317, 3.2381601, -19.084044, -21.669132, 3.716228, -19.084044, -21.527378, 3.992234, -19.084044, -21.586075, 4.0261383, -19.084044, -20.7394, 3.1794631, -19.084044, -21.04931, 3.1642156, -19.084044, -21.04931, 3.0964072, -19.084044, -21.586075, 4.0261383, -19.084044, -21.527378, 3.992234, -19.084044, -21.325317, 4.194296, -19.084044, -20.7394, 3.1794631, -19.084044, -20.773304, 3.2381601, -19.084044, -21.04931, 3.1642156, -19.084044, -21.586075, 4.0261383, -19.084044, -21.325317, 4.194296, -19.084044, -21.35922, 4.2529926, -18.82591, -20.542793, 3.4172263, -19.084044, -20.773304, 3.2381601, -19.084044, -20.7394, 3.1794631, -19.084044, -21.35922, 4.2529926, -19.084044, -21.325317, 4.194296, -19.084044, -21.04931, 4.26824, -18.82591, -20.542793, 3.4172263, -18.82591, -20.60149, 3.4511306, -19.084044, -20.773304, 3.2381601, -19.084044, -21.35922, 4.2529926, -19.084044, -21.04931, 4.26824, -19.084044, -21.04931, 4.3360486, -19.084044, -20.42949, 3.716228, -18.82591, -20.60149, 3.4511306, -18.82591, -20.542793, 3.4172263, -19.084044, -21.04931, 4.3360486, -19.084044, -21.04931, 4.26824, -19.084044, -20.773304, 4.194296, -19.084044, -20.42949, 3.716228, -19.084044, -20.4973, 3.716228, -18.82591, -20.60149, 3.4511306, -19.084044, -21.04931, 4.3360486, -19.084044, -20.773304, 4.194296, -19.084044, -20.7394, 4.2529926, -19.084044, -20.512547, 4.0261383, -19.084044, -20.4973, 3.716228, -19.084044, -20.42949, 3.716228, -19.084044, -20.7394, 4.2529926, -19.084044, -20.773304, 4.194296, -19.084044, -20.571243, 3.992234, -19.084044, -20.512547, 4.0261383, -19.084044, -20.571243, 3.992234, -19.084044, -20.4973, 3.716228, -19.084044, -20.7394, 4.2529926, -19.084044, -20.571243, 3.992234, -19.084044, -20.512547, 4.0261383, -13.55706, -21.527378, 3.4402227, -13.64679, -21.345833, 3.1714065, -13.605204, -21.345833, 3.1714065, -13.55706, -21.527378, 3.4402227, -13.598646, -21.527378, 3.4402227, -13.64679, -21.345833, 3.1714065, -13.55706, -21.601322, 3.716229, -13.598646, -21.527378, 3.4402227, -13.55706, -21.527378, 3.4402227, -13.55706, -21.601322, 3.716229, -13.598646, -21.601322, 3.716229, -13.598646, -21.527378, 3.4402227, -13.563242, -21.54901, 3.9200878, -13.598646, -21.601322, 3.716229, -13.55706, -21.601322, 3.716229, -13.563242, -21.54901, 3.9200878, -13.604641, -21.54901, 3.9200878, -13.598646, -21.601322, 3.716229, -13.55706, -21.325317, 4.194297, -13.604641, -21.54901, 3.9200878, -13.563242, -21.54901, 3.9200878, -13.55706, -21.325317, 4.194297, -13.598646, -21.325317, 4.194297, -13.604641, -21.54901, 3.9200878, -13.560621, -21.078318, 4.268241, -13.598646, -21.325317, 4.194297, -13.55706, -21.325317, 4.194297, -13.560621, -21.078318, 4.268241, -13.602205, -21.078318, 4.268241, -13.598646, -21.325317, 4.194297, -13.55706, -20.773304, 4.194297, -13.602205, -21.078318, 4.268241, -13.560621, -21.078318, 4.268241, -13.55706, -20.773304, 4.194297, -13.598646, -20.773304, 4.194297, -13.602205, -21.078318, 4.268241, -13.55706, -20.571243, 3.9922352, -13.598646, -20.773304, 4.194297, -13.55706, -20.773304, 4.194297, -13.55706, -20.571243, 3.9922352, -13.598646, -20.571243, 3.992235, -13.598646, -20.773304, 4.194297, -13.55706, -20.4973, 3.716229, -13.598646, -20.571243, 3.992235, -13.55706, -20.571243, 3.9922352, -13.55706, -20.4973, 3.716229, -13.598646, -20.4973, 3.716229, -13.598646, -20.571243, 3.992235, -13.55706, -20.571243, 3.4402227, -13.598646, -20.4973, 3.716229, -13.55706, -20.4973, 3.716229, -13.55706, -20.571243, 3.4402227, -13.598646, -20.571243, 3.4402227, -13.598646, -20.4973, 3.716229, -13.55706, -20.773304, 3.238161, -13.598646, -20.571243, 3.4402227, -13.55706, -20.571243, 3.4402227, -13.55706, -20.773304, 3.238161, -13.598646, -20.773304, 3.238161, -13.598646, -20.571243, 3.4402227, -13.55706, -21.04931, 3.1642165, -13.598646, -20.773304, 3.238161, -13.55706, -20.773304, 3.238161, -13.55706, -21.04931, 3.1642165, -13.598646, -21.04931, 3.1642165, -13.598646, -20.773304, 3.238161, -13.605204, -21.345833, 3.1714065, -13.598646, -21.04931, 3.1642165, -13.55706, -21.04931, 3.1642165, -13.605204, -21.345833, 3.1714065, -13.64679, -21.345833, 3.1714065, -13.598646, -21.04931, 3.1642165, -19.084044, -21.325317, 3.2381601, -19.042458, -21.527378, 3.4402218, -19.084044, -21.527378, 3.4402218, -19.084044, -21.325317, 3.2381601, -19.042458, -21.325317, 3.2381601, -19.042458, -21.527378, 3.4402218, -19.084044, -21.527378, 3.4402218, -19.042458, -21.601322, 3.716228, -19.084044, -21.601322, 3.716228, -19.084044, -21.527378, 3.4402218, -19.042458, -21.527378, 3.4402218, -19.042458, -21.601322, 3.716228, -19.084044, -21.601322, 3.716228, -19.042458, -21.527378, 3.992234, -19.084044, -21.527378, 3.992234, -19.084044, -21.601322, 3.716228, -19.042458, -21.601322, 3.716228, -19.042458, -21.527378, 3.992234, -19.084044, -21.527378, 3.992234, -19.042458, -21.325317, 4.194296, -19.084044, -21.325317, 4.194296, -19.084044, -21.527378, 3.992234, -19.042458, -21.527378, 3.992234, -19.042458, -21.325317, 4.194296, -19.084044, -21.325317, 4.194296, -19.042458, -21.04931, 4.26824, -19.084044, -21.04931, 4.26824, -19.084044, -21.325317, 4.194296, -19.042458, -21.325317, 4.194296, -19.042458, -21.04931, 4.26824, -19.084044, -21.04931, 4.26824, -19.042458, -21.04931, 4.26824, -19.042458, -20.773304, 4.194296, -19.084044, -21.04931, 4.26824, -19.042458, -20.773304, 4.194296, -19.084044, -20.773304, 4.194296, -19.084044, -20.773304, 4.194296, -19.042458, -20.571243, 3.992234, -19.084044, -20.571243, 3.992234, -19.084044, -20.773304, 4.194296, -19.042458, -20.773304, 4.194296, -19.042458, -20.571243, 3.992234, -19.084044, -20.571243, 3.992234, -19.042458, -20.4973, 3.716228, -19.084044, -20.4973, 3.716228, -19.084044, -20.571243, 3.992234, -19.042458, -20.571243, 3.992234, -19.042458, -20.4973, 3.716228, -19.084044, -20.4973, 3.716228, -18.784327, -20.60149, 3.4511306, -18.82591, -20.60149, 3.4511306, -19.084044, -20.4973, 3.716228, -19.042458, -20.4973, 3.716228, -18.784327, -20.60149, 3.4511306, -18.82591, -20.60149, 3.4511306, -19.042458, -20.773304, 3.2381601, -19.084044, -20.773304, 3.2381601, -18.82591, -20.60149, 3.4511306, -18.784327, -20.60149, 3.4511306, -19.042458, -20.773304, 3.2381601, -19.084044, -20.773304, 3.2381601, -19.042458, -21.04931, 3.1642156, -19.084044, -21.04931, 3.1642156, -19.084044, -20.773304, 3.2381601, -19.042458, -20.773304, 3.2381601, -19.042458, -21.04931, 3.1642156, -19.084044, -21.04931, 3.1642156, -19.042458, -21.04931, 3.1642156, -19.042458, -21.325317, 3.2381601, -19.084044, -21.04931, 3.1642156, -19.042458, -21.325317, 3.2381601, -19.084044, -21.325317, 3.2381601, -0.49230337, 3.2260702, -2.5408025, -0.2174604, 3.7041378, -2.5661154, -0.75557137, 3.4076157, -2.4682097, -0.75557137, 3.4076157, -2.4682097, -0.2174604, 3.7041378, -2.5661154, -0.7671461, 3.7041378, -2.5154896, -0.2174604, 3.1521254, -2.5661154, -0.2174604, 3.7041378, -2.5661154, -0.49230337, 3.2260702, -2.5408025, -0.7671461, 3.7041378, -2.5154896, -0.2174604, 3.7041378, -2.5661154, -0.6935134, 3.980144, -2.5222712, -0.01391077, 3.2044384, -2.5788422, -0.2174604, 3.7041378, -2.5661154, -0.2174604, 3.1521254, -2.5661154, -0.6935134, 3.980144, -2.5222712, -0.2174604, 3.7041378, -2.5661154, -0.49230337, 4.1822057, -2.5408025, 0.2585926, 3.4281316, -2.6099591, -0.2174604, 3.7041378, -2.5661154, -0.01391077, 3.2044384, -2.5788422, -0.49230337, 4.1822057, -2.5408025, -0.2174604, 3.7041378, -2.5661154, -0.2174604, 4.2561502, -2.5661154, 0.33255196, 3.6751301, -2.6131968, -0.2174604, 3.7041378, -2.5661154, 0.2585926, 3.4281316, -2.6099591, -0.2174604, 4.2561502, -2.5661154, -0.2174604, 3.7041378, -2.5661154, 0.057382584, 4.1822057, -2.5914283, 0.2585926, 3.980144, -2.6099591, -0.2174604, 3.7041378, -2.5661154, 0.33255196, 3.6751301, -2.6131968, 0.057382584, 4.1822057, -2.5914283, -0.2174604, 3.7041378, -2.5661154, 0.2585926, 3.980144, -2.6099591, -0.19425559, 3.4281316, 2.8985977, 0.2817974, 3.7041378, 2.8547535, 0.0069544315, 3.2260702, 2.8800664, 0.0069544315, 3.2260702, 2.8800664, 0.2817974, 3.7041378, 2.8547535, 0.2817974, 3.1521254, 2.8547535, -0.26788855, 3.7041378, 2.9053793, 0.2817974, 3.7041378, 2.8547535, -0.19425559, 3.4281316, 2.8985977, 0.2817974, 3.1521254, 2.8547535, 0.2817974, 3.7041378, 2.8547535, 0.55664027, 3.2260702, 2.8294406, -0.19425559, 3.980144, 2.8985977, 0.2817974, 3.7041378, 2.8547535, -0.26788855, 3.7041378, 2.9053793, 0.55664027, 3.2260702, 2.8294406, 0.2817974, 3.7041378, 2.8547535, 0.75785017, 3.4281316, 2.8109093, -0.0058562756, 4.1519585, 2.6220222, 0.2817974, 3.7041378, 2.8547535, -0.19425559, 3.980144, 2.8985977, 0.75785017, 3.4281316, 2.8109093, 0.2817974, 3.7041378, 2.8547535, 0.8314831, 3.7041378, 2.8041277, 0.2817974, 4.2561502, 2.8547535, 0.2817974, 3.7041378, 2.8547535, -0.0058562756, 4.1519585, 2.6220222, 0.8314831, 3.7041378, 2.8041277, 0.2817974, 3.7041378, 2.8547535, 0.75785017, 3.980144, 2.8109093, 0.55664027, 4.1822057, 2.8294406, 0.2817974, 3.7041378, 2.8547535, 0.2817974, 4.2561502, 2.8547535, 0.75785017, 3.980144, 2.8109093, 0.2817974, 3.7041378, 2.8547535, 0.55664027, 4.1822057, 2.8294406, -0.6072962, 3.4283175, -1.5901942, -0.41204357, 3.7041378, 1.3367851, -0.33841062, 3.4283175, 1.3300035, -0.33841062, 3.4283175, 1.3300035, -0.022993088, 3.1673732, 2.9245868, -0.1373241, 3.2263799, 1.3114836, -0.33841062, 3.4283175, 1.3300035, -0.24889135, 3.3942275, 2.9453917, -0.022993088, 3.1673732, 2.9245868, -0.41204357, 3.7041378, 1.3367851, -0.24889135, 3.3942275, 2.9453917, -0.33841062, 3.4283175, 1.3300035, -0.33841062, 3.4283175, 1.3300035, -0.1373241, 3.2263799, 1.3114836, -0.40790343, 3.2264419, -1.6264291, -0.33841062, 3.4283175, 1.3300035, -0.40790343, 3.2264419, -1.6264291, -0.6072962, 3.4283175, -1.5901942, -0.41204357, 3.7041378, 1.3367851, -0.33159733, 3.7041378, 2.9530087, -0.24889135, 3.3942275, 2.9453917, -0.81789684, 3.3737113, -2.5042315, -0.6072962, 3.4283175, -1.5901942, -0.40790343, 3.2264419, -1.6264291, -0.81789684, 3.3737113, -2.5042315, -0.40790343, 3.2264419, -1.6264291, -0.5298786, 3.1673732, -2.5791035, -0.83848286, 3.7041378, -2.5506816, -0.6072962, 3.4283175, -1.5901942, -0.81789684, 3.3737113, -2.5042315, -0.83848286, 3.7041378, -2.5506816, -0.6822865, 3.7041378, -1.5981488, -0.6072962, 3.4283175, -1.5901942, -0.6072962, 3.4283175, -1.5901942, -0.6822865, 3.7041378, -1.5981488, -0.41204357, 3.7041378, 1.3367851, -0.7557769, 4.014048, -2.5582986, -0.6822865, 3.7041378, -1.5981488, -0.83848286, 3.7041378, -2.5506816, -0.33841062, 3.979958, 1.3300035, -0.33159733, 3.7041378, 2.9530087, -0.41204357, 3.7041378, 1.3367851, -0.33841062, 3.979958, 1.3300035, -0.41204357, 3.7041378, 1.3367851, -0.6822865, 3.7041378, -1.5981488, -0.33841062, 3.979958, 1.3300035, -0.24889135, 4.014048, 2.9453917, -0.33159733, 3.7041378, 2.9530087, -0.1373241, 4.1818957, 1.3114836, -0.24889135, 4.014048, 2.9453917, -0.33841062, 3.979958, 1.3300035, -0.1373241, 4.1818957, 1.3114836, -0.035803795, 4.210655, 2.666542, -0.24889135, 4.014048, 2.9453917, -0.33841062, 3.979958, 1.3300035, -0.6822865, 3.7041378, -1.5981488, -0.6096499, 3.979958, -1.6157494, -0.1373241, 4.1818957, 1.3114836, -0.33841062, 3.979958, 1.3300035, -0.6096499, 3.979958, -1.6157494, -0.7557769, 4.014048, -2.5582986, -0.6096499, 3.979958, -1.6157494, -0.6822865, 3.7041378, -1.5981488, -0.1373241, 4.1818957, 1.3114836, -0.6096499, 3.979958, -1.6157494, -0.40862513, 4.1818337, -1.6342635, -0.5298786, 4.2409024, -2.5791035, -0.6096499, 3.979958, -1.6157494, -0.7557769, 4.014048, -2.5582986, -0.5298786, 4.2409024, -2.5791035, -0.40862513, 4.1818337, -1.6342635, -0.6096499, 3.979958, -1.6157494, -0.1373241, 3.2263799, 1.3114836, -0.13396716, 3.1524973, -1.6595592, -0.40790343, 3.2264419, -1.6264291, -0.5298786, 3.1673732, -2.5791035, -0.40790343, 3.2264419, -1.6264291, -0.13396716, 3.1524973, -1.6595592, -0.5298786, 3.1673732, -2.5791035, -0.13396716, 3.1524973, -1.6595592, -0.22127414, 3.0843172, -2.6075258, -0.22127414, 3.0843172, -2.6075258, -0.13396716, 3.1524973, -1.6595592, 0.14091396, 3.2264419, -1.68243, -0.22127414, 3.0843172, -2.6075258, 0.14091396, 3.2264419, -1.68243, 0.016053915, 3.1457415, -2.6231756, 0.13733363, 3.1524353, 1.2861879, 0.14091396, 3.2264419, -1.68243, -0.13396716, 3.1524973, -1.6595592, -0.1373241, 3.2263799, 1.3114836, 0.13733363, 3.1524353, 1.2861879, -0.13396716, 3.1524973, -1.6595592, 0.13733363, 3.1524353, 1.2861879, 0.4119916, 3.2263799, 1.2608922, 0.14091396, 3.2264419, -1.68243, 0.13733363, 3.1524353, 1.2861879, 0.5942155, 3.1673732, 2.867742, 0.4119916, 3.2263799, 1.2608922, 0.13733363, 3.1524353, 1.2861879, 0.28561115, 3.0843172, 2.8961644, 0.5942155, 3.1673732, 2.867742, -0.1373241, 3.2263799, 1.3114836, 0.28561115, 3.0843172, 2.8961644, 0.13733363, 3.1524353, 1.2861879, -0.1373241, 3.2263799, 1.3114836, -0.022993088, 3.1673732, 2.9245868, 0.28561115, 3.0843172, 2.8961644, 0.4119916, 3.2263799, 1.2608922, 0.34171534, 3.4283175, -1.7033694, 0.14091396, 3.2264419, -1.68243, 0.016053915, 3.1457415, -2.6231756, 0.14091396, 3.2264419, -1.68243, 0.34171534, 3.4283175, -1.7033694, 0.016053915, 3.1457415, -2.6231756, 0.34171534, 3.4283175, -1.7033694, 0.31322837, 3.3942275, -2.656753, 0.31322837, 3.3942275, -2.656753, 0.34171534, 3.4283175, -1.7033694, 0.4155202, 3.7041378, -1.7082856, 0.31322837, 3.3942275, -2.656753, 0.4155202, 3.7041378, -1.7082856, 0.39626074, 3.6751301, -2.6608262, 0.4119916, 3.2263799, 1.2608922, 0.613078, 3.4283175, 1.2423723, 0.34171534, 3.4283175, -1.7033694, 0.613078, 3.4283175, 1.2423723, 0.4155202, 3.7041378, -1.7082856, 0.34171534, 3.4283175, -1.7033694, 0.4119916, 3.2263799, 1.2608922, 0.8201138, 3.3942275, 2.8469372, 0.613078, 3.4283175, 1.2423723, 0.4119916, 3.2263799, 1.2608922, 0.5942155, 3.1673732, 2.867742, 0.8201138, 3.3942275, 2.8469372, 0.613078, 3.4283175, 1.2423723, 0.8201138, 3.3942275, 2.8469372, 0.90281975, 3.7041378, 2.8393202, 0.613078, 3.4283175, 1.2423723, 0.90281975, 3.7041378, 2.8393202, 0.68671095, 3.7041378, 1.2355907, 0.613078, 3.4283175, 1.2423723, 0.68671095, 3.7041378, 1.2355907, 0.4155202, 3.7041378, -1.7082856, 0.68671095, 3.7041378, 1.2355907, 0.90281975, 3.7041378, 2.8393202, 0.8201138, 4.014048, 2.8469372, 0.39626074, 3.6751301, -2.6608262, 0.4155202, 3.7041378, -1.7082856, 0.2700441, 3.9583263, -1.6948872, 0.68671095, 3.7041378, 1.2355907, 0.2700441, 3.9583263, -1.6948872, 0.4155202, 3.7041378, -1.7082856, 0.39626074, 3.6751301, -2.6608262, 0.2700441, 3.9583263, -1.6948872, 0.31322837, 4.014048, -2.656753, 0.68671095, 3.7041378, 1.2355907, 0.8201138, 4.014048, 2.8469372, 0.613078, 3.979958, 1.2423723, 0.68671095, 3.7041378, 1.2355907, 0.613078, 3.979958, 1.2423723, 0.2700441, 3.9583263, -1.6948872, 0.613078, 3.979958, 1.2423723, 0.8201138, 4.014048, 2.8469372, 0.5942155, 4.2409024, 2.867742, 0.613078, 3.979958, 1.2423723, 0.5942155, 4.2409024, 2.867742, 0.4119916, 4.1818957, 1.2608922, 0.613078, 3.979958, 1.2423723, 0.4119916, 4.1818957, 1.2608922, 0.14069057, 4.1818337, -1.684855, 0.613078, 3.979958, 1.2423723, 0.14069057, 4.1818337, -1.684855, 0.2700441, 3.9583263, -1.6948872, 0.31322837, 4.014048, -2.656753, 0.2700441, 3.9583263, -1.6948872, 0.14069057, 4.1818337, -1.684855, 0.31322837, 4.014048, -2.656753, 0.14069057, 4.1818337, -1.684855, 0.0873301, 4.2409024, -2.6359482, 0.4119916, 4.1818957, 1.2608922, -0.13396716, 4.2557163, -1.6595592, 0.14069057, 4.1818337, -1.684855, 0.0873301, 4.2409024, -2.6359482, 0.14069057, 4.1818337, -1.684855, -0.13396716, 4.2557163, -1.6595592, 0.0873301, 4.2409024, -2.6359482, -0.13396716, 4.2557163, -1.6595592, -0.22127414, 4.3239584, -2.6075258, -0.22127414, 4.3239584, -2.6075258, -0.13396716, 4.2557163, -1.6595592, -0.40862513, 4.1818337, -1.6342635, -0.22127414, 4.3239584, -2.6075258, -0.40862513, 4.1818337, -1.6342635, -0.5298786, 4.2409024, -2.5791035, 0.13733363, 4.2558403, 1.2861879, -0.40862513, 4.1818337, -1.6342635, -0.13396716, 4.2557163, -1.6595592, 0.4119916, 4.1818957, 1.2608922, 0.13733363, 4.2558403, 1.2861879, -0.13396716, 4.2557163, -1.6595592, 0.13733363, 4.2558403, 1.2861879, -0.1373241, 4.1818957, 1.3114836, -0.40862513, 4.1818337, -1.6342635, 0.13733363, 4.2558403, 1.2861879, -0.035803795, 4.210655, 2.666542, -0.1373241, 4.1818957, 1.3114836, 0.13733363, 4.2558403, 1.2861879, 0.28561115, 4.3239584, 2.8961644, -0.035803795, 4.210655, 2.666542, 0.4119916, 4.1818957, 1.2608922, 0.28561115, 4.3239584, 2.8961644, 0.13733363, 4.2558403, 1.2861879, 0.4119916, 4.1818957, 1.2608922, 0.5942155, 4.2409024, 2.867742, 0.28561115, 4.3239584, 2.8961644, -0.24889135, 3.3942275, 2.9453917, -0.022993088, 3.1673732, 2.9245868, -0.022993088, 3.1673732, 2.9245868, -0.022993088, 3.1673732, 2.9245868, -0.022993088, 3.1673732, 2.9245868, 0.28561115, 3.0843172, 2.8961644, -0.24889135, 3.3942275, 2.9453917, -0.24889135, 3.3942275, 2.9453917, -0.022993088, 3.1673732, 2.9245868, -0.33159733, 3.7041378, 2.9530087, -0.24889135, 3.3942275, 2.9453917, -0.24889135, 3.3942275, 2.9453917, -0.33159733, 3.7041378, 2.9530087, -0.33159733, 3.7041378, 2.9530087, -0.24889135, 3.3942275, 2.9453917, -0.022993088, 3.1673732, 2.9245868, 0.28561115, 3.0843172, 2.8961644, 0.28561115, 3.0843172, 2.8961644, 0.28561115, 3.0843172, 2.8961644, 0.28561115, 3.0843172, 2.8961644, 0.5942155, 3.1673732, 2.867742, 0.28561115, 3.0843172, 2.8961644, 0.5942155, 3.1673732, 2.867742, 0.5942155, 3.1673732, 2.867742, 0.5942155, 3.1673732, 2.867742, 0.5942155, 3.1673732, 2.867742, 0.8201138, 3.3942275, 2.8469372, 0.5942155, 3.1673732, 2.867742, 0.8201138, 3.3942275, 2.8469372, 0.8201138, 3.3942275, 2.8469372, 0.8201138, 3.3942275, 2.8469372, 0.8201138, 3.3942275, 2.8469372, 0.90281975, 3.7041378, 2.8393202, 0.8201138, 3.3942275, 2.8469372, 0.90281975, 3.7041378, 2.8393202, 0.90281975, 3.7041378, 2.8393202, 0.90281975, 3.7041378, 2.8393202, 0.90281975, 3.7041378, 2.8393202, 0.8201138, 4.014048, 2.8469372, 0.90281975, 3.7041378, 2.8393202, 0.8201138, 4.014048, 2.8469372, 0.8201138, 4.014048, 2.8469372, 0.8201138, 4.014048, 2.8469372, 0.8201138, 4.014048, 2.8469372, 0.5942155, 4.2409024, 2.867742, 0.8201138, 4.014048, 2.8469372, 0.5942155, 4.2409024, 2.867742, 0.5942155, 4.2409024, 2.867742, 0.5942155, 4.2409024, 2.867742, 0.5942155, 4.2409024, 2.867742, 0.28561115, 4.3239584, 2.8961644, 0.5942155, 4.2409024, 2.867742, 0.28561115, 4.3239584, 2.8961644, 0.28561115, 4.3239584, 2.8961644, 0.28561115, 4.3239584, 2.8961644, 0.28561115, 4.3239584, 2.8961644, -0.035803795, 4.210655, 2.666542, 0.28561115, 4.3239584, 2.8961644, -0.035803795, 4.210655, 2.666542, -0.035803795, 4.210655, 2.666542, -0.035803795, 4.210655, 2.666542, -0.24889135, 4.014048, 2.9453917, -0.24889135, 4.014048, 2.9453917, -0.035803795, 4.210655, 2.666542, -0.035803795, 4.210655, 2.666542, -0.24889135, 4.014048, 2.9453917, -0.24889135, 4.014048, 2.9453917, -0.33159733, 3.7041378, 2.9530087, -0.33159733, 3.7041378, 2.9530087, -0.24889135, 4.014048, 2.9453917, -0.24889135, 4.014048, 2.9453917, -0.33159733, 3.7041378, 2.9530087, -0.5298786, 3.1673732, -2.5791035, -0.75938535, 3.4076157, -2.5096202, -0.81789684, 3.3737113, -2.5042315, -0.81789684, 3.3737113, -2.5042315, -0.75938535, 3.4076157, -2.5096202, -0.7709601, 3.7041378, -2.5569, -0.5298786, 3.1673732, -2.5791035, -0.49611712, 3.2260702, -2.582213, -0.75938535, 3.4076157, -2.5096202, -0.81789684, 3.3737113, -2.5042315, -0.7709601, 3.7041378, -2.5569, -0.83848286, 3.7041378, -2.5506816, -0.22127414, 3.0843172, -2.6075258, -0.49611712, 3.2260702, -2.582213, -0.5298786, 3.1673732, -2.5791035, -0.83848286, 3.7041378, -2.5506816, -0.7709601, 3.7041378, -2.5569, -0.69732714, 3.980144, -2.5636816, -0.22127414, 3.0843172, -2.6075258, -0.22127414, 3.1521254, -2.6075258, -0.49611712, 3.2260702, -2.582213, -0.83848286, 3.7041378, -2.5506816, -0.69732714, 3.980144, -2.5636816, -0.7557769, 4.014048, -2.5582986, 0.016053915, 3.1457415, -2.6231756, -0.22127414, 3.1521254, -2.6075258, -0.22127414, 3.0843172, -2.6075258, -0.7557769, 4.014048, -2.5582986, -0.69732714, 3.980144, -2.5636816, -0.49611712, 4.1822057, -2.582213, 0.016053915, 3.1457415, -2.6231756, -0.017707348, 3.2044384, -2.6200662, -0.22127414, 3.1521254, -2.6075258, -0.7557769, 4.014048, -2.5582986, -0.49611712, 4.1822057, -2.582213, -0.5298786, 4.2409024, -2.5791035, 0.31322837, 3.3942275, -2.656753, -0.017707348, 3.2044384, -2.6200662, 0.016053915, 3.1457415, -2.6231756, -0.5298786, 4.2409024, -2.5791035, -0.49611712, 4.1822057, -2.582213, -0.22127414, 4.2561502, -2.6075258, 0.31322837, 3.3942275, -2.656753, 0.25477862, 3.4281316, -2.65137, -0.017707348, 3.2044384, -2.6200662, -0.5298786, 4.2409024, -2.5791035, -0.22127414, 4.2561502, -2.6075258, -0.22127414, 4.3239584, -2.6075258, 0.39626074, 3.6751301, -2.6608262, 0.25477862, 3.4281316, -2.65137, 0.31322837, 3.3942275, -2.656753, -0.22127414, 4.3239584, -2.6075258, -0.22127414, 4.2561502, -2.6075258, 0.05356884, 4.1822057, -2.6328387, 0.39626074, 3.6751301, -2.6608262, 0.32873797, 3.6751301, -2.6546073, 0.25477862, 3.4281316, -2.65137, -0.22127414, 4.3239584, -2.6075258, 0.05356884, 4.1822057, -2.6328387, 0.0873301, 4.2409024, -2.6359482, 0.31322837, 4.014048, -2.656753, 0.32873797, 3.6751301, -2.6546073, 0.39626074, 3.6751301, -2.6608262, 0.0873301, 4.2409024, -2.6359482, 0.05356884, 4.1822057, -2.6328387, 0.25477862, 3.980144, -2.65137, 0.31322837, 4.014048, -2.656753, 0.25477862, 3.980144, -2.65137, 0.32873797, 3.6751301, -2.6546073, 0.0873301, 4.2409024, -2.6359482, 0.25477862, 3.980144, -2.65137, 0.31322837, 4.014048, -2.656753, -0.24889135, 3.3942275, 2.9453917, 0.010768175, 3.2260702, 2.9214773, -0.022993088, 3.1673732, 2.9245868, -0.022993088, 3.1673732, 2.9245868, 0.010768175, 3.2260702, 2.9214773, 0.28561115, 3.1521254, 2.8961644, -0.24889135, 3.3942275, 2.9453917, -0.19044161, 3.4281316, 2.9400086, 0.010768175, 3.2260702, 2.9214773, -0.022993088, 3.1673732, 2.9245868, 0.28561115, 3.1521254, 2.8961644, 0.28561115, 3.0843172, 2.8961644, -0.33159733, 3.7041378, 2.9530087, -0.19044161, 3.4281316, 2.9400086, -0.24889135, 3.3942275, 2.9453917, 0.28561115, 3.0843172, 2.8961644, 0.28561115, 3.1521254, 2.8961644, 0.56045413, 3.2260702, 2.8708515, -0.33159733, 3.7041378, 2.9530087, -0.2640748, 3.7041378, 2.9467902, -0.19044161, 3.4281316, 2.9400086, 0.28561115, 3.0843172, 2.8961644, 0.56045413, 3.2260702, 2.8708515, 0.5942155, 3.1673732, 2.867742, -0.24889135, 4.014048, 2.9453917, -0.2640748, 3.7041378, 2.9467902, -0.33159733, 3.7041378, 2.9530087, 0.5942155, 3.1673732, 2.867742, 0.56045413, 3.2260702, 2.8708515, 0.76166415, 3.4281316, 2.8523202, -0.24889135, 4.014048, 2.9453917, -0.19044161, 3.980144, 2.9400086, -0.2640748, 3.7041378, 2.9467902, 0.5942155, 3.1673732, 2.867742, 0.76166415, 3.4281316, 2.8523202, 0.8201138, 3.3942275, 2.8469372, -0.035803795, 4.210655, 2.666542, -0.19044161, 3.980144, 2.9400086, -0.24889135, 4.014048, 2.9453917, 0.8201138, 3.3942275, 2.8469372, 0.76166415, 3.4281316, 2.8523202, 0.8352971, 3.7041378, 2.8455386, -0.035803795, 4.210655, 2.666542, -0.002042532, 4.1519585, 2.6634326, -0.19044161, 3.980144, 2.9400086, 0.8201138, 3.3942275, 2.8469372, 0.8352971, 3.7041378, 2.8455386, 0.90281975, 3.7041378, 2.8393202, 0.28561115, 4.3239584, 2.8961644, -0.002042532, 4.1519585, 2.6634326, -0.035803795, 4.210655, 2.666542, 0.90281975, 3.7041378, 2.8393202, 0.8352971, 3.7041378, 2.8455386, 0.76166415, 3.980144, 2.8523202, 0.28561115, 4.3239584, 2.8961644, 0.28561115, 4.2561502, 2.8961644, -0.002042532, 4.1519585, 2.6634326, 0.90281975, 3.7041378, 2.8393202, 0.76166415, 3.980144, 2.8523202, 0.8201138, 4.014048, 2.8469372, 0.5942155, 4.2409024, 2.867742, 0.28561115, 4.2561502, 2.8961644, 0.28561115, 4.3239584, 2.8961644, 0.8201138, 4.014048, 2.8469372, 0.76166415, 3.980144, 2.8523202, 0.56045413, 4.1822057, 2.8708515, 0.5942155, 4.2409024, 2.867742, 0.56045413, 4.1822057, 2.8708515, 0.28561115, 4.2561502, 2.8961644, 0.8201138, 4.014048, 2.8469372, 0.56045413, 4.1822057, 2.8708515, 0.5942155, 4.2409024, 2.867742, -0.49611712, 3.2260702, -2.582213, -0.75557137, 3.4076157, -2.4682097, -0.75938535, 3.4076157, -2.5096202, -0.49611712, 3.2260702, -2.582213, -0.49230337, 3.2260702, -2.5408025, -0.75557137, 3.4076157, -2.4682097, -0.22127414, 3.1521254, -2.6075258, -0.49230337, 3.2260702, -2.5408025, -0.49611712, 3.2260702, -2.582213, -0.22127414, 3.1521254, -2.6075258, -0.2174604, 3.1521254, -2.5661154, -0.49230337, 3.2260702, -2.5408025, -0.017707348, 3.2044384, -2.6200662, -0.2174604, 3.1521254, -2.5661154, -0.22127414, 3.1521254, -2.6075258, -0.017707348, 3.2044384, -2.6200662, -0.01391077, 3.2044384, -2.5788422, -0.2174604, 3.1521254, -2.5661154, 0.25477862, 3.4281316, -2.65137, -0.01391077, 3.2044384, -2.5788422, -0.017707348, 3.2044384, -2.6200662, 0.25477862, 3.4281316, -2.65137, 0.2585926, 3.4281316, -2.6099591, -0.01391077, 3.2044384, -2.5788422, 0.32873797, 3.6751301, -2.6546073, 0.2585926, 3.4281316, -2.6099591, 0.25477862, 3.4281316, -2.65137, 0.32873797, 3.6751301, -2.6546073, 0.33255196, 3.6751301, -2.6131968, 0.2585926, 3.4281316, -2.6099591, 0.25477862, 3.980144, -2.65137, 0.33255196, 3.6751301, -2.6131968, 0.32873797, 3.6751301, -2.6546073, 0.25477862, 3.980144, -2.65137, 0.2585926, 3.980144, -2.6099591, 0.33255196, 3.6751301, -2.6131968, 0.05356884, 4.1822057, -2.6328387, 0.2585926, 3.980144, -2.6099591, 0.25477862, 3.980144, -2.65137, 0.05356884, 4.1822057, -2.6328387, 0.057382584, 4.1822057, -2.5914283, 0.2585926, 3.980144, -2.6099591, -0.22127414, 4.2561502, -2.6075258, 0.057382584, 4.1822057, -2.5914283, 0.05356884, 4.1822057, -2.6328387, -0.22127414, 4.2561502, -2.6075258, -0.2174604, 4.2561502, -2.5661154, 0.057382584, 4.1822057, -2.5914283, -0.49611712, 4.1822057, -2.582213, -0.2174604, 4.2561502, -2.5661154, -0.22127414, 4.2561502, -2.6075258, -0.49611712, 4.1822057, -2.582213, -0.49230337, 4.1822057, -2.5408025, -0.2174604, 4.2561502, -2.5661154, -0.69732714, 3.980144, -2.5636816, -0.49230337, 4.1822057, -2.5408025, -0.49611712, 4.1822057, -2.582213, -0.69732714, 3.980144, -2.5636816, -0.6935134, 3.980144, -2.5222712, -0.49230337, 4.1822057, -2.5408025, -0.7709601, 3.7041378, -2.5569, -0.6935134, 3.980144, -2.5222712, -0.69732714, 3.980144, -2.5636816, -0.7709601, 3.7041378, -2.5569, -0.7671461, 3.7041378, -2.5154896, -0.6935134, 3.980144, -2.5222712, -0.75938535, 3.4076157, -2.5096202, -0.7671461, 3.7041378, -2.5154896, -0.7709601, 3.7041378, -2.5569, -0.75938535, 3.4076157, -2.5096202, -0.75557137, 3.4076157, -2.4682097, -0.7671461, 3.7041378, -2.5154896, -0.19044161, 3.4281316, 2.9400086, 0.0069544315, 3.2260702, 2.8800664, 0.010768175, 3.2260702, 2.9214773, -0.19044161, 3.4281316, 2.9400086, -0.19425559, 3.4281316, 2.8985977, 0.0069544315, 3.2260702, 2.8800664, 0.010768175, 3.2260702, 2.9214773, 0.2817974, 3.1521254, 2.8547535, 0.28561115, 3.1521254, 2.8961644, 0.010768175, 3.2260702, 2.9214773, 0.0069544315, 3.2260702, 2.8800664, 0.2817974, 3.1521254, 2.8547535, 0.28561115, 3.1521254, 2.8961644, 0.55664027, 3.2260702, 2.8294406, 0.56045413, 3.2260702, 2.8708515, 0.28561115, 3.1521254, 2.8961644, 0.2817974, 3.1521254, 2.8547535, 0.55664027, 3.2260702, 2.8294406, 0.56045413, 3.2260702, 2.8708515, 0.75785017, 3.4281316, 2.8109093, 0.76166415, 3.4281316, 2.8523202, 0.56045413, 3.2260702, 2.8708515, 0.55664027, 3.2260702, 2.8294406, 0.75785017, 3.4281316, 2.8109093, 0.76166415, 3.4281316, 2.8523202, 0.8314831, 3.7041378, 2.8041277, 0.8352971, 3.7041378, 2.8455386, 0.76166415, 3.4281316, 2.8523202, 0.75785017, 3.4281316, 2.8109093, 0.8314831, 3.7041378, 2.8041277, 0.8352971, 3.7041378, 2.8455386, 0.8314831, 3.7041378, 2.8041277, 0.75785017, 3.980144, 2.8109093, 0.8352971, 3.7041378, 2.8455386, 0.75785017, 3.980144, 2.8109093, 0.76166415, 3.980144, 2.8523202, 0.76166415, 3.980144, 2.8523202, 0.55664027, 4.1822057, 2.8294406, 0.56045413, 4.1822057, 2.8708515, 0.76166415, 3.980144, 2.8523202, 0.75785017, 3.980144, 2.8109093, 0.55664027, 4.1822057, 2.8294406, 0.56045413, 4.1822057, 2.8708515, 0.2817974, 4.2561502, 2.8547535, 0.28561115, 4.2561502, 2.8961644, 0.56045413, 4.1822057, 2.8708515, 0.55664027, 4.1822057, 2.8294406, 0.2817974, 4.2561502, 2.8547535, 0.28561115, 4.2561502, 2.8961644, -0.0058562756, 4.1519585, 2.6220222, -0.002042532, 4.1519585, 2.6634326, 0.28561115, 4.2561502, 2.8961644, 0.2817974, 4.2561502, 2.8547535, -0.0058562756, 4.1519585, 2.6220222, -0.002042532, 4.1519585, 2.6634326, -0.19425559, 3.980144, 2.8985977, -0.19044161, 3.980144, 2.9400086, -0.002042532, 4.1519585, 2.6634326, -0.0058562756, 4.1519585, 2.6220222, -0.19425559, 3.980144, 2.8985977, -0.19044161, 3.980144, 2.9400086, -0.26788855, 3.7041378, 2.9053793, -0.2640748, 3.7041378, 2.9467902, -0.19044161, 3.980144, 2.9400086, -0.19425559, 3.980144, 2.8985977, -0.26788855, 3.7041378, 2.9053793, -0.2640748, 3.7041378, 2.9467902, -0.26788855, 3.7041378, 2.9053793, -0.19425559, 3.4281316, 2.8985977, -0.2640748, 3.7041378, 2.9467902, -0.19425559, 3.4281316, 2.8985977, -0.19044161, 3.4281316, 2.9400086, -5.555945, -21.30843, 0.44781768, -3.54707, -21.129436, 0.3570392, -5.555945, -21.129436, 0.44781768, -5.555945, -21.30843, 0.44781768, -3.54707, -21.30843, 0.3570392, -3.54707, -21.129436, 0.3570392, -7.629112, -21.129436, 0.3570392, -5.555945, -21.30843, 0.44781768, -5.555945, -21.129436, 0.44781768, -7.629112, -21.129436, 0.3570392, -7.629112, -21.30843, 0.3570392, -5.555945, -21.30843, 0.44781768, -5.555945, -21.30843, 0.9271282, -3.54707, -21.30843, 0.3570392, -5.555945, -21.30843, 0.44781768, -5.555945, -21.30843, 0.9271282, -3.54707, -21.30843, 0.7632997, -3.54707, -21.30843, 0.3570392, -5.555945, -21.30843, 0.44781768, -7.629112, -21.30843, 0.91196275, -5.555945, -21.30843, 0.9271282, -5.555945, -21.30843, 0.44781768, -7.629112, -21.30843, 0.62980187, -7.629112, -21.30843, 0.91196275, -7.629112, -21.30843, 0.62980187, -5.555945, -21.30843, 0.44781768, -7.629112, -21.30843, 0.3570392, -3.54707, -21.30843, 0.7632997, -3.54707, -21.129436, 0.3570392, -3.54707, -21.30843, 0.3570392, -3.54707, -21.30843, 0.7632997, -3.54707, -21.129435, 0.7632997, -3.54707, -21.129436, 0.3570392, -7.629112, -21.129436, 0.5971217, -7.5507226, -21.183048, 0.6447537, -7.629112, -21.30843, 0.62980187, -5.555945, -21.129435, 0.80730057, -5.555945, -21.174292, 0.9271282, -6.0741305, -21.129435, 0.92328346, -5.555945, -21.129435, 0.80730057, -5.0537796, -21.129435, 0.8861177, -5.555945, -21.174292, 0.9271282, -5.555945, -21.129435, 0.80730057, -3.54707, -21.129435, 0.7632997, -5.0537796, -21.129435, 0.8861177, -5.555945, -21.129435, 0.80730057, -3.54707, -21.129436, 0.3570392, -3.54707, -21.129435, 0.7632997, -3.54707, -21.129436, 0.3570392, -5.555945, -21.129435, 0.80730057, -5.555945, -21.129436, 0.44781768, -6.0741305, -21.129435, 0.92328346, -5.555945, -21.129436, 0.44781768, -5.555945, -21.129435, 0.80730057, -5.555945, -21.129436, 0.44781768, -7.629112, -21.129436, 0.5971217, -7.629112, -21.129436, 0.3570392, -5.555945, -21.129436, 0.44781768, -7.2726197, -21.129435, 0.6462488, -7.629112, -21.129436, 0.5971217, -6.0741305, -21.129435, 0.92328346, -7.2726197, -21.129435, 0.6462488, -5.555945, -21.129436, 0.44781768, -7.5507226, -21.183048, 0.6447537, -7.629112, -21.129436, 0.5971217, -7.2726197, -21.129435, 0.6462488, -7.629112, -21.129435, 0.7325418, -7.5507226, -21.183048, 0.6447537, -7.2726197, -21.129435, 0.6462488, -7.2726197, -21.129435, 0.6462488, -6.0741305, -21.129435, 0.92328346, -7.629112, -21.129435, 0.7325418, -7.629112, -21.129435, 0.7325418, -6.0741305, -21.129435, 0.92328346, -7.629112, -21.129435, 0.91196275, -7.629112, -21.30843, 0.3570392, -7.629112, -21.129436, 0.5971217, -7.629112, -21.30843, 0.62980187, -7.629112, -21.30843, 0.3570392, -7.629112, -21.129436, 0.3570392, -7.629112, -21.129436, 0.5971217, -7.629112, -21.129435, 0.7325418, -7.629112, -21.30843, 0.91196275, -7.629112, -21.30843, 0.62980187, -7.629112, -21.129435, 0.7325418, -7.629112, -21.129435, 0.91196275, -7.629112, -21.30843, 0.91196275, -7.5507226, -21.183048, 0.6447537, -7.629112, -21.129435, 0.7325418, -7.629112, -21.30843, 0.62980187, -7.629112, -21.129435, 0.91196275, -5.555945, -21.30843, 0.9271282, -7.629112, -21.30843, 0.91196275, -5.555945, -21.30843, 0.9271282, -7.629112, -21.129435, 0.91196275, -6.0741305, -21.129435, 0.92328346, -5.555945, -21.30843, 0.9271282, -6.0741305, -21.129435, 0.92328346, -5.555945, -21.174292, 0.9271282, -5.555945, -21.174292, 0.9271282, -3.54707, -21.30843, 0.7632997, -5.555945, -21.30843, 0.9271282, -5.555945, -21.174292, 0.9271282, -3.54707, -21.129435, 0.7632997, -3.54707, -21.30843, 0.7632997, -3.54707, -21.129435, 0.7632997, -5.555945, -21.174292, 0.9271282, -5.0537796, -21.129435, 0.8861177, -3.988361, -21.129435, 2.1034038, -3.54707, -21.30843, 2.1130157, -3.988361, -21.30843, 2.1034038, -3.988361, -21.129435, 2.1034038, -3.54707, -21.129435, 2.1130157, -3.54707, -21.30843, 2.1130157, -3.988361, -21.30843, 2.1034038, -6.7189775, -21.129435, 2.110666, -3.988361, -21.129435, 2.1034038, -3.988361, -21.30843, 2.1034038, -7.629112, -21.17429, 2.1130157, -6.7189775, -21.129435, 2.110666, -7.629112, -21.17429, 2.1130157, -3.988361, -21.30843, 2.1034038, -7.629112, -21.30843, 2.1130157, -3.54707, -21.129435, 1.6317828, -3.988361, -21.129435, 2.1034038, -3.988361, -21.129435, 1.6206758, -6.7189775, -21.129435, 2.110666, -3.988361, -21.129435, 1.6206758, -3.988361, -21.129435, 2.1034038, -6.7189775, -21.129435, 2.110666, -7.629112, -21.129435, 1.7067552, -3.988361, -21.129435, 1.6206758, -7.629112, -21.129435, 1.7067552, -6.7189775, -21.129435, 2.110666, -7.629112, -21.129435, 2.0113437, -7.629112, -21.129435, 2.0113437, -6.7189775, -21.129435, 2.110666, -7.629112, -21.17429, 2.1130157, -3.988361, -21.129435, 2.1034038, -3.54707, -21.129435, 1.6317828, -3.78224, -21.129435, 1.8853219, -3.78224, -21.129435, 1.8853219, -3.54707, -21.129435, 1.6317828, -3.54707, -21.129435, 1.8257284, -3.54707, -21.129435, 1.8257284, -3.5872264, -21.197786, 1.8791275, -3.78224, -21.129435, 1.8853219, -3.5872264, -21.197786, 1.8791275, -3.54707, -21.129435, 1.959867, -3.78224, -21.129435, 1.8853219, -3.988361, -21.129435, 2.1034038, -3.78224, -21.129435, 1.8853219, -3.54707, -21.129435, 1.959867, -3.988361, -21.129435, 2.1034038, -3.54707, -21.129435, 1.959867, -3.54707, -21.129435, 2.1130157, -3.988361, -21.30843, 1.6206758, -3.54707, -21.129435, 1.6317828, -3.988361, -21.129435, 1.6206758, -3.988361, -21.30843, 1.6206758, -3.54707, -21.30843, 1.6317829, -3.54707, -21.129435, 1.6317828, -7.629112, -21.129435, 1.7067552, -3.988361, -21.30843, 1.6206758, -3.988361, -21.129435, 1.6206758, -7.629112, -21.129435, 1.7067552, -7.629112, -21.30843, 1.7067553, -3.988361, -21.30843, 1.6206758, -3.54707, -21.30843, 2.1130157, -3.988361, -21.30843, 1.6206758, -3.988361, -21.30843, 2.1034038, -3.988361, -21.30843, 1.6206758, -3.54707, -21.30843, 2.1130157, -3.54707, -21.30843, 1.8528552, -3.988361, -21.30843, 1.6206758, -3.54707, -21.30843, 1.8528552, -3.54707, -21.30843, 1.6317829, -7.629112, -21.30843, 1.7067553, -3.988361, -21.30843, 2.1034038, -3.988361, -21.30843, 1.6206758, -7.629112, -21.30843, 1.7067553, -7.629112, -21.30843, 2.1130157, -3.988361, -21.30843, 2.1034038, -3.54707, -21.129435, 1.959867, -3.5872264, -21.197786, 1.8791275, -3.54707, -21.30843, 1.8528552, -3.5872264, -21.197786, 1.8791275, -3.54707, -21.129435, 1.8257284, -3.54707, -21.30843, 1.8528552, -3.54707, -21.30843, 2.1130157, -3.54707, -21.129435, 1.959867, -3.54707, -21.30843, 1.8528552, -3.54707, -21.30843, 2.1130157, -3.54707, -21.129435, 2.1130157, -3.54707, -21.129435, 1.959867, -3.54707, -21.129435, 1.8257284, -3.54707, -21.30843, 1.6317829, -3.54707, -21.30843, 1.8528552, -3.54707, -21.129435, 1.8257284, -3.54707, -21.129435, 1.6317828, -3.54707, -21.30843, 1.6317829, -7.629112, -21.129435, 1.7067552, -7.629112, -21.30843, 2.1130157, -7.629112, -21.30843, 1.7067553, -7.629112, -21.30843, 2.1130157, -7.629112, -21.129435, 1.7067552, -7.629112, -21.129435, 2.0113437, -7.629112, -21.30843, 2.1130157, -7.629112, -21.129435, 2.0113437, -7.629112, -21.17429, 2.1130157, -3.9170194, -21.320818, -0.44629717, -3.8958735, -21.335342, -0.95978284, -3.8958735, -21.34047, -0.43775296, -3.9170194, -21.320818, -0.44629717, -3.9170194, -21.31612, -0.95081186, -3.8958735, -21.335342, -0.95978284, -3.9170194, -21.31612, -0.95081186, -3.8958735, -21.210388, -0.979434, -3.8958735, -21.335342, -0.95978284, -3.9170194, -21.31612, -0.95081186, -3.9170194, -21.215515, -0.9587152, -3.8958735, -21.210388, -0.979434, -3.8958735, -21.34047, -0.43775296, -3.9170194, -21.35478, -0.9687538, -3.9170194, -21.36012, -0.4289956, -3.8958735, -21.34047, -0.43775296, -3.8958735, -21.335342, -0.95978284, -3.9170194, -21.35478, -0.9687538, -3.8958735, -21.335342, -0.95978284, -3.9170194, -21.205263, -1.0001528, -3.9170194, -21.35478, -0.9687538, -3.8958735, -21.335342, -0.95978284, -3.8958735, -21.210388, -0.979434, -3.9170194, -21.205263, -1.0001528, -3.9170194, -21.36012, -0.4289956, -3.9385924, -21.335342, -0.95978284, -3.9385924, -21.34047, -0.43775296, -3.9170194, -21.36012, -0.4289956, -3.9170194, -21.35478, -0.9687538, -3.9385924, -21.335342, -0.95978284, -3.9170194, -21.35478, -0.9687538, -3.9385924, -21.210388, -0.979434, -3.9385924, -21.335342, -0.95978284, -3.9170194, -21.35478, -0.9687538, -3.9170194, -21.205263, -1.0001528, -3.9385924, -21.210388, -0.979434, -3.9385924, -21.34047, -0.43775296, -3.9170194, -21.31612, -0.95081186, -3.9170194, -21.320818, -0.44629717, -3.9385924, -21.34047, -0.43775296, -3.9385924, -21.335342, -0.95978284, -3.9170194, -21.31612, -0.95081186, -3.9385924, -21.335342, -0.95978284, -3.9170194, -21.215515, -0.9587152, -3.9170194, -21.31612, -0.95081186, -3.9385924, -21.335342, -0.95978284, -3.9385924, -21.210388, -0.979434, -3.9170194, -21.215515, -0.9587152, -3.9170194, -21.215515, -0.9587152, -3.8958735, -21.105513, -0.94718075, -3.8958735, -21.210388, -0.979434, -3.9170194, -21.215515, -0.9587152, -3.9170194, -21.126446, -0.9426954, -3.8958735, -21.105513, -0.94718075, -3.9170194, -21.126446, -0.9426954, -3.8958735, -21.10359, -0.44181156, -3.8958735, -21.105513, -0.94718075, -3.9170194, -21.126446, -0.9426954, -3.9170194, -21.12431, -0.44693804, -3.8958735, -21.10359, -0.44181156, -3.9170194, -21.12431, -0.44693804, -3.8958735, -21.219788, -0.39460683, -3.8958735, -21.10359, -0.44181156, -3.9170194, -21.12431, -0.44693804, -3.9170194, -21.22406, -0.4155395, -3.8958735, -21.219788, -0.39460683, -3.8958735, -21.210388, -0.979434, -3.9170194, -21.084795, -0.95188, -3.9170194, -21.205263, -1.0001528, -3.8958735, -21.210388, -0.979434, -3.8958735, -21.105513, -0.94718075, -3.9170194, -21.084795, -0.95188, -3.8958735, -21.105513, -0.94718075, -3.9170194, -21.082659, -0.43668532, -3.9170194, -21.084795, -0.95188, -3.8958735, -21.105513, -0.94718075, -3.8958735, -21.10359, -0.44181156, -3.9170194, -21.082659, -0.43668532, -3.8958735, -21.10359, -0.44181156, -3.9170194, -21.215515, -0.3736744, -3.9170194, -21.082659, -0.43668532, -3.8958735, -21.10359, -0.44181156, -3.8958735, -21.219788, -0.39460683, -3.9170194, -21.215515, -0.3736744, -3.9170194, -21.205263, -1.0001528, -3.9385924, -21.105513, -0.94718075, -3.9385924, -21.210388, -0.979434, -3.9170194, -21.205263, -1.0001528, -3.9170194, -21.084795, -0.95188, -3.9385924, -21.105513, -0.94718075, -3.9385924, -21.210388, -0.979434, -3.9170194, -21.126446, -0.9426954, -3.9170194, -21.215515, -0.9587152, -3.9385924, -21.210388, -0.979434, -3.9385924, -21.105513, -0.94718075, -3.9170194, -21.126446, -0.9426954, -3.9385924, -21.105513, -0.94718075, -3.9170194, -21.12431, -0.44693804, -3.9170194, -21.126446, -0.9426954, -3.9385924, -21.105513, -0.94718075, -3.9385924, -21.10359, -0.44181156, -3.9170194, -21.12431, -0.44693804, -3.9385924, -21.10359, -0.44181156, -3.9170194, -21.22406, -0.4155395, -3.9170194, -21.12431, -0.44693804, -3.9385924, -21.10359, -0.44181156, -3.9385924, -21.219788, -0.39460683, -3.9170194, -21.22406, -0.4155395, -3.9170194, -21.084795, -0.95188, -3.9385924, -21.10359, -0.44181156, -3.9385924, -21.105513, -0.94718075, -3.9170194, -21.084795, -0.95188, -3.9170194, -21.082659, -0.43668532, -3.9385924, -21.10359, -0.44181156, -3.9170194, -21.082659, -0.43668532, -3.9385924, -21.219788, -0.39460683, -3.9385924, -21.10359, -0.44181156, -3.9170194, -21.082659, -0.43668532, -3.9170194, -21.215515, -0.3736744, -3.9385924, -21.219788, -0.39460683, -3.9170194, -21.22406, -0.4155395, -3.8958735, -21.34047, -0.43775296, -3.8958735, -21.219788, -0.39460683, -3.9170194, -21.22406, -0.4155395, -3.9170194, -21.320818, -0.44629717, -3.8958735, -21.34047, -0.43775296, -3.8958735, -21.219788, -0.39460683, -3.9170194, -21.36012, -0.4289956, -3.9170194, -21.215515, -0.3736744, -3.8958735, -21.219788, -0.39460683, -3.8958735, -21.34047, -0.43775296, -3.9170194, -21.36012, -0.4289956, -3.9170194, -21.215515, -0.3736744, -3.9385924, -21.34047, -0.43775296, -3.9385924, -21.219788, -0.39460683, -3.9170194, -21.215515, -0.3736744, -3.9170194, -21.36012, -0.4289956, -3.9385924, -21.34047, -0.43775296, -3.9385924, -21.219788, -0.39460683, -3.9170194, -21.320818, -0.44629717, -3.9170194, -21.22406, -0.4155395, -3.9385924, -21.219788, -0.39460683, -3.9385924, -21.34047, -0.43775296, -3.9170194, -21.320818, -0.44629717, -7.464643, -21.40476, 5.450674, -7.3512235, -21.359264, 5.450674, -7.3512235, -21.40476, 5.450674, -7.464643, -21.40476, 5.450674, -7.464643, -21.359264, 5.450674, -7.3512235, -21.359264, 5.450674, -7.464643, -21.359264, 5.450674, -7.3512235, -21.359266, -1.0281336, -7.3512235, -21.359264, 5.450674, -7.464643, -21.359264, 5.450674, -7.464643, -21.359266, -1.0281336, -7.3512235, -21.359266, -1.0281336, -7.464643, -21.359266, -1.0281336, -7.3512235, -21.404762, -1.0281336, -7.3512235, -21.359266, -1.0281336, -7.464643, -21.359266, -1.0281336, -7.464643, -21.404762, -1.0281336, -7.3512235, -21.404762, -1.0281336, -7.464643, -21.404762, -1.0281336, -7.3512235, -21.40476, 5.450674, -7.3512235, -21.404762, -1.0281336, -7.464643, -21.404762, -1.0281336, -7.464643, -21.40476, 5.450674, -7.3512235, -21.40476, 5.450674, -7.3512235, -21.40476, 5.450674, -7.3512235, -21.359266, -1.0281336, -7.3512235, -21.404762, -1.0281336, -7.3512235, -21.40476, 5.450674, -7.3512235, -21.359264, 5.450674, -7.3512235, -21.359266, -1.0281336, -7.464643, -21.404762, -1.0281336, -7.464643, -21.359264, 5.450674, -7.464643, -21.40476, 5.450674, -7.464643, -21.404762, -1.0281336, -7.464643, -21.359266, -1.0281336, -7.464643, -21.359264, 5.450674, -6.9564967, -21.30843, -0.21347666, -3.4046016, -21.129436, -0.29165316, -6.9564967, -21.129436, -0.2134769, -6.9564967, -21.30843, -0.21347666, -3.4046016, -21.30843, -0.29165292, -3.4046016, -21.129436, -0.29165316, -7.7713675, -21.129436, -0.21689463, -6.9564967, -21.30843, -0.21347666, -6.9564967, -21.129436, -0.2134769, -7.7713675, -21.129436, -0.21689463, -7.7713675, -21.30843, -0.21689439, -6.9564967, -21.30843, -0.21347666, -6.9564967, -21.30843, 0.28014445, -3.4046016, -21.30843, -0.29165292, -6.9564967, -21.30843, -0.21347666, -6.9564967, -21.30843, 0.28014445, -3.4046016, -21.30843, 0.1893661, -3.4046016, -21.30843, -0.29165292, -7.7713675, -21.30843, -0.21689439, -6.9564967, -21.30843, 0.28014445, -6.9564967, -21.30843, -0.21347666, -7.7713675, -21.30843, -0.21689439, -7.7713675, -21.30843, 0.1893661, -6.9564967, -21.30843, 0.28014445, -7.7713675, -21.30843, -0.21689439, -7.7713675, -21.129436, 0.18936586, -7.7713675, -21.30843, 0.1893661, -7.7713675, -21.30843, -0.21689439, -7.7713675, -21.129436, -0.21689463, -7.7713675, -21.129436, 0.18936586, -6.9564967, -21.129436, 0.15668559, -6.9564967, -21.174292, 0.28014445, -7.1602683, -21.129436, 0.25750327, -6.9564967, -21.129436, 0.15668559, -6.0685763, -21.129436, 0.25750327, -6.9564967, -21.174292, 0.28014445, -6.9564967, -21.129436, 0.15668559, -4.0705953, -21.129436, 0.20624006, -6.0685763, -21.129436, 0.25750327, -7.1602683, -21.129436, 0.25750327, -6.9564967, -21.129436, -0.2134769, -6.9564967, -21.129436, 0.15668559, -7.1602683, -21.129436, 0.25750327, -7.7713675, -21.129436, -0.21689463, -6.9564967, -21.129436, -0.2134769, -7.7713675, -21.129436, -0.21689463, -7.1602683, -21.129436, 0.25750327, -7.7713675, -21.129436, 0.18936586, -3.4046016, -21.129436, -0.29165316, -6.9564967, -21.129436, 0.15668559, -6.9564967, -21.129436, -0.2134769, -6.9564967, -21.129436, 0.15668559, -3.4046016, -21.129436, -0.29165316, -4.0705953, -21.129436, 0.20624006, -4.0705953, -21.129436, 0.20624006, -3.4046016, -21.129436, -0.29165316, -3.4046016, -21.129436, 0.06911111, -4.0705953, -21.129436, 0.20624006, -3.4046016, -21.129436, 0.06911111, -3.4046016, -21.174292, 0.18936586, -7.7713675, -21.129436, 0.18936586, -6.9564967, -21.30843, 0.28014445, -7.7713675, -21.30843, 0.1893661, -6.9564967, -21.30843, 0.28014445, -7.7713675, -21.129436, 0.18936586, -7.1602683, -21.129436, 0.25750327, -6.9564967, -21.30843, 0.28014445, -7.1602683, -21.129436, 0.25750327, -6.9564967, -21.174292, 0.28014445, -6.9564967, -21.174292, 0.28014445, -3.4046016, -21.30843, 0.1893661, -6.9564967, -21.30843, 0.28014445, -6.9564967, -21.174292, 0.28014445, -4.0705953, -21.129436, 0.20624006, -3.4046016, -21.30843, 0.1893661, -3.4046016, -21.30843, 0.1893661, -4.0705953, -21.129436, 0.20624006, -3.4046016, -21.174292, 0.18936586, -4.0705953, -21.129436, 0.20624006, -6.9564967, -21.174292, 0.28014445, -6.0685763, -21.129436, 0.25750327, -3.4046016, -21.174292, 0.18936586, -3.4046016, -21.30843, -0.29165292, -3.4046016, -21.30843, 0.1893661, -3.4046016, -21.30843, -0.29165292, -3.4046016, -21.174292, 0.18936586, -3.4046016, -21.129436, 0.06911111, -3.4046016, -21.30843, -0.29165292, -3.4046016, -21.129436, 0.06911111, -3.4046016, -21.129436, -0.29165316, -7.7320657, -21.3975, -1.0330465, -7.351437, -19.082968, -0.9484632, -7.4360213, -21.3975, -1.0330465, -7.7320657, -21.3975, -1.0330465, -7.81665, -19.082968, -0.9484632, -7.351437, -19.082968, -0.9484632, -7.81665, -19.082968, -0.9484632, -7.351437, -19.082968, -1.4134626, -7.351437, -19.082968, -0.9484632, -7.81665, -19.082968, -0.9484632, -7.81665, -19.082968, -1.4134626, -7.351437, -19.082968, -1.4134626, -7.81665, -19.082968, -1.4134626, -7.4360213, -21.3975, -1.3288774, -7.351437, -19.082968, -1.4134626, -7.81665, -19.082968, -1.4134626, -7.7320657, -21.3975, -1.3288774, -7.4360213, -21.3975, -1.3288774, -7.7320657, -21.3975, -1.3288774, -7.4360213, -21.3975, -1.0330465, -7.4360213, -21.3975, -1.3288774, -7.7320657, -21.3975, -1.3288774, -7.7320657, -21.3975, -1.0330465, -7.4360213, -21.3975, -1.0330465, -7.4360213, -21.3975, -1.0330465, -7.351437, -19.082968, -1.4134626, -7.4360213, -21.3975, -1.3288774, -7.4360213, -21.3975, -1.0330465, -7.351437, -19.082968, -0.9484632, -7.351437, -19.082968, -1.4134626, -7.7320657, -21.3975, -1.3288774, -7.81665, -19.082968, -0.9484632, -7.7320657, -21.3975, -1.0330465, -7.7320657, -21.3975, -1.3288774, -7.81665, -19.082968, -1.4134626, -7.81665, -19.082968, -0.9484632, -3.988361, -21.129436, -0.4571905, -3.54707, -21.30843, -0.4477923, -3.988361, -21.30843, -0.45719028, -3.988361, -21.129436, -0.4571905, -3.54707, -21.129436, -0.44779253, -3.54707, -21.30843, -0.4477923, -3.988361, -21.30843, -0.45719028, -6.7189775, -21.129436, -0.4501419, -3.988361, -21.129436, -0.4571905, -3.988361, -21.30843, -0.45719028, -7.629112, -21.174292, -0.44779253, -6.7189775, -21.129436, -0.4501419, -7.629112, -21.174292, -0.44779253, -3.988361, -21.30843, -0.45719028, -7.629112, -21.30843, -0.4477923, -3.54707, -21.129436, -0.92881155, -3.988361, -21.129436, -0.4571905, -3.988361, -21.129436, -0.9401324, -6.7189775, -21.129436, -0.4501419, -3.988361, -21.129436, -0.9401324, -3.988361, -21.129436, -0.4571905, -6.7189775, -21.129436, -0.4501419, -7.629112, -21.129436, -0.8540528, -3.988361, -21.129436, -0.9401324, -7.629112, -21.129436, -0.8540528, -6.7189775, -21.129436, -0.4501419, -7.629112, -21.129436, -0.5492506, -7.629112, -21.129436, -0.5492506, -6.7189775, -21.129436, -0.4501419, -7.629112, -21.174292, -0.44779253, -3.988361, -21.129436, -0.4571905, -3.54707, -21.129436, -0.92881155, -3.78224, -21.129436, -0.6754861, -3.78224, -21.129436, -0.6754861, -3.54707, -21.129436, -0.92881155, -3.54707, -21.129436, -0.7350795, -3.54707, -21.129436, -0.7350795, -3.5872264, -21.197786, -0.68168044, -3.78224, -21.129436, -0.6754861, -3.5872264, -21.197786, -0.68168044, -3.54707, -21.129436, -0.6009412, -3.78224, -21.129436, -0.6754861, -3.988361, -21.129436, -0.4571905, -3.78224, -21.129436, -0.6754861, -3.54707, -21.129436, -0.6009412, -3.988361, -21.129436, -0.4571905, -3.54707, -21.129436, -0.6009412, -3.54707, -21.129436, -0.44779253, -3.988361, -21.30843, -0.94013214, -3.54707, -21.129436, -0.92881155, -3.988361, -21.129436, -0.9401324, -3.988361, -21.30843, -0.94013214, -3.54707, -21.30843, -0.9288113, -3.54707, -21.129436, -0.92881155, -7.629112, -21.129436, -0.8540528, -3.988361, -21.30843, -0.94013214, -3.988361, -21.129436, -0.9401324, -7.629112, -21.129436, -0.8540528, -7.629112, -21.30843, -0.85405254, -3.988361, -21.30843, -0.94013214, -3.54707, -21.30843, -0.4477923, -3.988361, -21.30843, -0.94013214, -3.988361, -21.30843, -0.45719028, -3.988361, -21.30843, -0.94013214, -3.54707, -21.30843, -0.4477923, -3.54707, -21.30843, -0.7079525, -3.988361, -21.30843, -0.94013214, -3.54707, -21.30843, -0.7079525, -3.54707, -21.30843, -0.9288113, -7.629112, -21.30843, -0.85405254, -3.988361, -21.30843, -0.45719028, -3.988361, -21.30843, -0.94013214, -7.629112, -21.30843, -0.85405254, -7.629112, -21.30843, -0.4477923, -3.988361, -21.30843, -0.45719028, -3.54707, -21.129436, -0.6009412, -3.5872264, -21.197786, -0.68168044, -3.54707, -21.30843, -0.7079525, -3.5872264, -21.197786, -0.68168044, -3.54707, -21.129436, -0.7350795, -3.54707, -21.30843, -0.7079525, -3.54707, -21.30843, -0.4477923, -3.54707, -21.129436, -0.6009412, -3.54707, -21.30843, -0.7079525, -3.54707, -21.30843, -0.4477923, -3.54707, -21.129436, -0.44779253, -3.54707, -21.129436, -0.6009412, -3.54707, -21.129436, -0.7350795, -3.54707, -21.30843, -0.9288113, -3.54707, -21.30843, -0.7079525, -3.54707, -21.129436, -0.7350795, -3.54707, -21.129436, -0.92881155, -3.54707, -21.30843, -0.9288113, -7.629112, -21.129436, -0.8540528, -7.629112, -21.30843, -0.4477923, -7.629112, -21.30843, -0.85405254, -7.629112, -21.30843, -0.4477923, -7.629112, -21.129436, -0.8540528, -7.629112, -21.129436, -0.5492506, -7.629112, -21.30843, -0.4477923, -7.629112, -21.129436, -0.5492506, -7.629112, -21.174292, -0.44779253, -3.8766494, -21.129435, 2.6811824, -3.4046016, -21.308428, 2.6905806, -3.8766494, -21.308428, 2.6811824, -3.8766494, -21.129435, 2.6811824, -3.4046016, -21.129435, 2.6905804, -3.4046016, -21.308428, 2.6905806, -3.8766494, -21.308428, 2.6811824, -6.797795, -21.129435, 2.688231, -3.8766494, -21.129435, 2.6811824, -3.8766494, -21.308428, 2.6811824, -7.7713675, -21.17429, 2.6905804, -6.797795, -21.129435, 2.688231, -7.7713675, -21.17429, 2.6905804, -3.8766494, -21.308428, 2.6811824, -7.7713675, -21.308428, 2.6905806, -3.4046016, -21.129435, 2.2095613, -3.8766494, -21.129435, 2.6811824, -3.8766494, -21.129435, 2.1984541, -6.797795, -21.129435, 2.688231, -3.8766494, -21.129435, 2.1984541, -3.8766494, -21.129435, 2.6811824, -6.797795, -21.129435, 2.688231, -7.7713675, -21.129435, 2.28432, -3.8766494, -21.129435, 2.1984541, -7.7713675, -21.129435, 2.28432, -6.797795, -21.129435, 2.688231, -7.7713675, -21.129435, 2.5891223, -7.7713675, -21.129435, 2.5891223, -6.797795, -21.129435, 2.688231, -7.7713675, -21.17429, 2.6905804, -3.8766494, -21.129435, 2.6811824, -3.4046016, -21.129435, 2.2095613, -3.656218, -21.129435, 2.4631002, -3.656218, -21.129435, 2.4631002, -3.4046016, -21.129435, 2.2095613, -3.4046016, -21.129435, 2.4032931, -3.4046016, -21.129435, 2.4032931, -3.4477482, -21.197786, 2.4566922, -3.656218, -21.129435, 2.4631002, -3.4477482, -21.197786, 2.4566922, -3.4046016, -21.129435, 2.5376453, -3.656218, -21.129435, 2.4631002, -3.8766494, -21.129435, 2.6811824, -3.656218, -21.129435, 2.4631002, -3.4046016, -21.129435, 2.5376453, -3.8766494, -21.129435, 2.6811824, -3.4046016, -21.129435, 2.5376453, -3.4046016, -21.129435, 2.6905804, -3.8766494, -21.30843, 2.1984544, -3.4046016, -21.129435, 2.2095613, -3.8766494, -21.129435, 2.1984541, -3.8766494, -21.30843, 2.1984544, -3.4046016, -21.30843, 2.2095613, -3.4046016, -21.129435, 2.2095613, -7.7713675, -21.129435, 2.28432, -3.8766494, -21.30843, 2.1984544, -3.8766494, -21.129435, 2.1984541, -7.7713675, -21.129435, 2.28432, -7.7713675, -21.30843, 2.28432, -3.8766494, -21.30843, 2.1984544, -3.4046016, -21.308428, 2.6905806, -3.8766494, -21.30843, 2.1984544, -3.8766494, -21.308428, 2.6811824, -3.8766494, -21.30843, 2.1984544, -3.4046016, -21.308428, 2.6905806, -3.4046016, -21.308428, 2.4306335, -3.8766494, -21.30843, 2.1984544, -3.4046016, -21.308428, 2.4306335, -3.4046016, -21.30843, 2.2095613, -7.7713675, -21.30843, 2.28432, -3.8766494, -21.308428, 2.6811824, -3.8766494, -21.30843, 2.1984544, -7.7713675, -21.30843, 2.28432, -7.7713675, -21.308428, 2.6905806, -3.8766494, -21.308428, 2.6811824, -3.4046016, -21.129435, 2.5376453, -3.4477482, -21.197786, 2.4566922, -3.4046016, -21.308428, 2.4306335, -3.4477482, -21.197786, 2.4566922, -3.4046016, -21.129435, 2.4032931, -3.4046016, -21.308428, 2.4306335, -3.4046016, -21.308428, 2.6905806, -3.4046016, -21.129435, 2.5376453, -3.4046016, -21.308428, 2.4306335, -3.4046016, -21.308428, 2.6905806, -3.4046016, -21.129435, 2.6905804, -3.4046016, -21.129435, 2.5376453, -3.4046016, -21.129435, 2.4032931, -3.4046016, -21.30843, 2.2095613, -3.4046016, -21.308428, 2.4306335, -3.4046016, -21.129435, 2.4032931, -3.4046016, -21.129435, 2.2095613, -3.4046016, -21.30843, 2.2095613, -7.7713675, -21.129435, 2.28432, -7.7713675, -21.308428, 2.6905806, -7.7713675, -21.30843, 2.28432, -7.7713675, -21.308428, 2.6905806, -7.7713675, -21.129435, 2.28432, -7.7713675, -21.129435, 2.5891223, -7.7713675, -21.308428, 2.6905806, -7.7713675, -21.129435, 2.5891223, -7.7713675, -21.17429, 2.6905804, -5.5538087, -21.308428, 2.944333, -3.4046016, -21.129435, 2.853768, -5.5538087, -21.129435, 2.944333, -5.5538087, -21.308428, 2.944333, -3.4046016, -21.308428, 2.8537683, -3.4046016, -21.129435, 2.853768, -7.7713675, -21.129435, 2.853768, -5.5538087, -21.308428, 2.944333, -5.5538087, -21.129435, 2.944333, -7.7713675, -21.129435, 2.853768, -7.7713675, -21.308428, 2.8537683, -5.5538087, -21.308428, 2.944333, -5.5538087, -21.308428, 3.4236436, -3.4046016, -21.308428, 2.8537683, -5.5538087, -21.308428, 2.944333, -5.5538087, -21.308428, 3.4236436, -3.4046016, -21.308428, 3.2600288, -3.4046016, -21.308428, 2.8537683, -5.5538087, -21.308428, 2.944333, -7.7713675, -21.308428, 3.4084783, -5.5538087, -21.308428, 3.4236436, -5.5538087, -21.308428, 2.944333, -7.7713675, -21.308428, 3.126531, -7.7713675, -21.308428, 3.4084783, -7.7713675, -21.308428, 3.126531, -5.5538087, -21.308428, 2.944333, -7.7713675, -21.308428, 2.8537683, -3.4046016, -21.308428, 3.2600288, -3.4046016, -21.129435, 2.853768, -3.4046016, -21.308428, 2.8537683, -3.4046016, -21.308428, 3.2600288, -3.4046016, -21.129435, 3.2600288, -3.4046016, -21.129435, 2.853768, -7.7713675, -21.129435, 3.0938506, -7.687638, -21.183048, 3.1414826, -7.7713675, -21.308428, 3.126531, -5.5538087, -21.129435, 3.3038158, -5.5538087, -21.17429, 3.4236436, -6.108092, -21.129435, 3.4197989, -5.5538087, -21.129435, 3.3038158, -5.0164003, -21.129435, 3.3828466, -5.5538087, -21.17429, 3.4236436, -5.5538087, -21.129435, 3.3038158, -3.4046016, -21.129435, 3.2600288, -5.0164003, -21.129435, 3.3828466, -5.5538087, -21.129435, 3.3038158, -3.4046016, -21.129435, 2.853768, -3.4046016, -21.129435, 3.2600288, -3.4046016, -21.129435, 2.853768, -5.5538087, -21.129435, 3.3038158, -5.5538087, -21.129435, 2.944333, -6.108092, -21.129435, 3.4197989, -5.5538087, -21.129435, 2.944333, -5.5538087, -21.129435, 3.3038158, -5.5538087, -21.129435, 2.944333, -7.7713675, -21.129435, 3.0938506, -7.7713675, -21.129435, 2.853768, -5.5538087, -21.129435, 2.944333, -7.3903117, -21.129435, 3.142764, -7.7713675, -21.129435, 3.0938506, -6.108092, -21.129435, 3.4197989, -7.3903117, -21.129435, 3.142764, -5.5538087, -21.129435, 2.944333, -7.687638, -21.183048, 3.1414826, -7.7713675, -21.129435, 3.0938506, -7.3903117, -21.129435, 3.142764, -7.7713675, -21.129435, 3.2290573, -7.687638, -21.183048, 3.1414826, -7.3903117, -21.129435, 3.142764, -7.3903117, -21.129435, 3.142764, -6.108092, -21.129435, 3.4197989, -7.7713675, -21.129435, 3.2290573, -7.7713675, -21.129435, 3.2290573, -6.108092, -21.129435, 3.4197989, -7.7713675, -21.129435, 3.4084783, -7.7713675, -21.308428, 2.8537683, -7.7713675, -21.129435, 3.0938506, -7.7713675, -21.308428, 3.126531, -7.7713675, -21.308428, 2.8537683, -7.7713675, -21.129435, 2.853768, -7.7713675, -21.129435, 3.0938506, -7.7713675, -21.129435, 3.2290573, -7.7713675, -21.308428, 3.4084783, -7.7713675, -21.308428, 3.126531, -7.7713675, -21.129435, 3.2290573, -7.7713675, -21.129435, 3.4084783, -7.7713675, -21.308428, 3.4084783, -7.687638, -21.183048, 3.1414826, -7.7713675, -21.129435, 3.2290573, -7.7713675, -21.308428, 3.126531, -7.7713675, -21.129435, 3.4084783, -5.5538087, -21.308428, 3.4236436, -7.7713675, -21.308428, 3.4084783, -5.5538087, -21.308428, 3.4236436, -7.7713675, -21.129435, 3.4084783, -6.108092, -21.129435, 3.4197989, -5.5538087, -21.308428, 3.4236436, -6.108092, -21.129435, 3.4197989, -5.5538087, -21.17429, 3.4236436, -5.5538087, -21.17429, 3.4236436, -3.4046016, -21.308428, 3.2600288, -5.5538087, -21.308428, 3.4236436, -5.5538087, -21.17429, 3.4236436, -3.4046016, -21.129435, 3.2600288, -3.4046016, -21.308428, 3.2600288, -3.4046016, -21.129435, 3.2600288, -5.5538087, -21.17429, 3.4236436, -5.0164003, -21.129435, 3.3828466, -3.988361, -21.129435, 4.050549, -3.54707, -21.308428, 4.0599475, -3.988361, -21.308428, 4.050549, -3.988361, -21.129435, 4.050549, -3.54707, -21.129435, 4.0599475, -3.54707, -21.308428, 4.0599475, -3.988361, -21.308428, 4.050549, -6.7189775, -21.129435, 4.057598, -3.988361, -21.129435, 4.050549, -3.988361, -21.308428, 4.050549, -7.629112, -21.17429, 4.0599475, -6.7189775, -21.129435, 4.057598, -7.629112, -21.17429, 4.0599475, -3.988361, -21.308428, 4.050549, -7.629112, -21.308428, 4.0599475, -3.54707, -21.129435, 3.5789282, -3.988361, -21.129435, 4.050549, -3.988361, -21.129435, 3.567821, -6.7189775, -21.129435, 4.057598, -3.988361, -21.129435, 3.567821, -3.988361, -21.129435, 4.050549, -6.7189775, -21.129435, 4.057598, -7.629112, -21.129435, 3.653687, -3.988361, -21.129435, 3.567821, -7.629112, -21.129435, 3.653687, -6.7189775, -21.129435, 4.057598, -7.629112, -21.129435, 3.958489, -7.629112, -21.129435, 3.958489, -6.7189775, -21.129435, 4.057598, -7.629112, -21.17429, 4.0599475, -3.988361, -21.129435, 4.050549, -3.54707, -21.129435, 3.5789282, -3.78224, -21.129435, 3.8322535, -3.78224, -21.129435, 3.8322535, -3.54707, -21.129435, 3.5789282, -3.54707, -21.129435, 3.7726603, -3.54707, -21.129435, 3.7726603, -3.5872264, -21.197784, 3.8260593, -3.78224, -21.129435, 3.8322535, -3.5872264, -21.197784, 3.8260593, -3.54707, -21.129435, 3.9070125, -3.78224, -21.129435, 3.8322535, -3.988361, -21.129435, 4.050549, -3.78224, -21.129435, 3.8322535, -3.54707, -21.129435, 3.9070125, -3.988361, -21.129435, 4.050549, -3.54707, -21.129435, 3.9070125, -3.54707, -21.129435, 4.0599475, -3.988361, -21.308428, 3.567821, -3.54707, -21.129435, 3.5789282, -3.988361, -21.129435, 3.567821, -3.988361, -21.308428, 3.567821, -3.54707, -21.308428, 3.5789282, -3.54707, -21.129435, 3.5789282, -7.629112, -21.129435, 3.653687, -3.988361, -21.308428, 3.567821, -3.988361, -21.129435, 3.567821, -7.629112, -21.129435, 3.653687, -7.629112, -21.308428, 3.653687, -3.988361, -21.308428, 3.567821, -3.54707, -21.308428, 4.0599475, -3.988361, -21.308428, 3.567821, -3.988361, -21.308428, 4.050549, -3.988361, -21.308428, 3.567821, -3.54707, -21.308428, 4.0599475, -3.54707, -21.308428, 3.799787, -3.988361, -21.308428, 3.567821, -3.54707, -21.308428, 3.799787, -3.54707, -21.308428, 3.5789282, -7.629112, -21.308428, 3.653687, -3.988361, -21.308428, 4.050549, -3.988361, -21.308428, 3.567821, -7.629112, -21.308428, 3.653687, -7.629112, -21.308428, 4.0599475, -3.988361, -21.308428, 4.050549, -3.54707, -21.129435, 3.9070125, -3.5872264, -21.197784, 3.8260593, -3.54707, -21.308428, 3.799787, -3.5872264, -21.197784, 3.8260593, -3.54707, -21.129435, 3.7726603, -3.54707, -21.308428, 3.799787, -3.54707, -21.308428, 4.0599475, -3.54707, -21.129435, 3.9070125, -3.54707, -21.308428, 3.799787, -3.54707, -21.308428, 4.0599475, -3.54707, -21.129435, 4.0599475, -3.54707, -21.129435, 3.9070125, -3.54707, -21.129435, 3.7726603, -3.54707, -21.308428, 3.5789282, -3.54707, -21.308428, 3.799787, -3.54707, -21.129435, 3.7726603, -3.54707, -21.129435, 3.5789282, -3.54707, -21.308428, 3.5789282, -7.629112, -21.129435, 3.653687, -7.629112, -21.308428, 4.0599475, -7.629112, -21.308428, 3.653687, -7.629112, -21.308428, 4.0599475, -7.629112, -21.129435, 3.653687, -7.629112, -21.129435, 3.958489, -7.629112, -21.308428, 4.0599475, -7.629112, -21.129435, 3.958489, -7.629112, -21.17429, 4.0599475, -3.950554, -21.3975, -1.0022886, -3.6320815, -19.082968, -1.0409505, -3.5934205, -21.3975, -1.0022886, -3.950554, -21.3975, -1.0022886, -3.911893, -19.082968, -1.0409505, -3.6320815, -19.082968, -1.0409505, -3.911893, -19.082968, -1.0409505, -3.6320815, -19.082968, -1.3209753, -3.6320815, -19.082968, -1.0409505, -3.911893, -19.082968, -1.0409505, -3.911893, -19.082968, -1.3209753, -3.6320815, -19.082968, -1.3209753, -3.911893, -19.082968, -1.3209753, -3.5934205, -21.3975, -1.3596354, -3.6320815, -19.082968, -1.3209753, -3.911893, -19.082968, -1.3209753, -3.950554, -21.3975, -1.3596354, -3.5934205, -21.3975, -1.3596354, -3.950554, -21.3975, -1.3596354, -3.5934205, -21.3975, -1.0022886, -3.5934205, -21.3975, -1.3596354, -3.950554, -21.3975, -1.3596354, -3.950554, -21.3975, -1.0022886, -3.5934205, -21.3975, -1.0022886, -3.5934205, -21.3975, -1.0022886, -3.6320815, -19.082968, -1.3209753, -3.5934205, -21.3975, -1.3596354, -3.5934205, -21.3975, -1.0022886, -3.6320815, -19.082968, -1.0409505, -3.6320815, -19.082968, -1.3209753, -3.950554, -21.3975, -1.3596354, -3.911893, -19.082968, -1.0409505, -3.950554, -21.3975, -1.0022886, -3.950554, -21.3975, -1.3596354, -3.911893, -19.082968, -1.3209753, -3.911893, -19.082968, -1.0409505, -7.8010573, -21.397497, 5.803108, -7.4439244, -19.082966, 5.8031073, -7.4439244, -21.397497, 5.803108, -7.8010573, -21.397497, 5.803108, -7.8010573, -19.082966, 5.8031073, -7.4439244, -19.082966, 5.8031073, -7.8010573, -21.397497, 5.4459753, -7.4439244, -21.397497, 5.803108, -7.4439244, -21.397497, 5.4459753, -7.8010573, -21.397497, 5.4459753, -7.8010573, -21.397497, 5.803108, -7.4439244, -21.397497, 5.803108, -7.4439244, -21.397497, 5.803108, -7.4439244, -19.082966, 5.4459743, -7.4439244, -21.397497, 5.4459753, -7.4439244, -21.397497, 5.803108, -7.4439244, -19.082966, 5.8031073, -7.4439244, -19.082966, 5.4459743, -7.8010573, -19.661598, 5.4459743, -7.7117743, -19.082966, 5.4459743, -7.8010573, -19.082966, 5.5352573, -7.8010573, -21.397497, 5.803108, -7.8010573, -19.661598, 5.4459743, -7.8010573, -19.082966, 5.5352573, -7.8010573, -19.661598, 5.4459743, -7.8010573, -21.397497, 5.803108, -7.8010573, -21.397497, 5.4459753, -7.8010573, -21.397497, 5.803108, -7.8010573, -19.082966, 5.5352573, -7.8010573, -19.082966, 5.8031073, -7.8010573, -19.082966, 5.5352573, -7.4439244, -19.082966, 5.8031073, -7.8010573, -19.082966, 5.8031073, -7.4439244, -19.082966, 5.8031073, -7.8010573, -19.082966, 5.5352573, -7.7117743, -19.082966, 5.4459743, -7.4439244, -19.082966, 5.8031073, -7.7117743, -19.082966, 5.4459743, -7.4439244, -19.082966, 5.4459743, -7.8010573, -21.397497, 5.4459753, -7.7117743, -19.082966, 5.4459743, -7.8010573, -19.661598, 5.4459743, -7.7117743, -19.082966, 5.4459743, -7.8010573, -21.397497, 5.4459753, -7.4439244, -21.397497, 5.4459753, -7.7117743, -19.082966, 5.4459743, -7.4439244, -21.397497, 5.4459753, -7.4439244, -19.082966, 5.4459743, -5.555945, -21.308428, 4.955558, -3.54707, -21.129435, 4.8647785, -5.555945, -21.129435, 4.9555573, -5.555945, -21.308428, 4.955558, -3.54707, -21.308428, 4.864779, -3.54707, -21.129435, 4.8647785, -7.629112, -21.129435, 4.8647785, -5.555945, -21.308428, 4.955558, -5.555945, -21.129435, 4.9555573, -7.629112, -21.129435, 4.8647785, -7.629112, -21.308428, 4.864779, -5.555945, -21.308428, 4.955558, -5.555945, -21.308428, 5.434868, -3.54707, -21.308428, 4.864779, -5.555945, -21.308428, 4.955558, -5.555945, -21.308428, 5.434868, -3.54707, -21.308428, 5.2710395, -3.54707, -21.308428, 4.864779, -5.555945, -21.308428, 4.955558, -7.629112, -21.308428, 5.4197025, -5.555945, -21.308428, 5.434868, -5.555945, -21.308428, 4.955558, -7.629112, -21.308428, 5.137542, -7.629112, -21.308428, 5.4197025, -7.629112, -21.308428, 5.137542, -5.555945, -21.308428, 4.955558, -7.629112, -21.308428, 4.864779, -3.54707, -21.308428, 5.2710395, -3.54707, -21.129435, 4.8647785, -3.54707, -21.308428, 4.864779, -3.54707, -21.308428, 5.2710395, -3.54707, -21.129435, 5.271039, -3.54707, -21.129435, 4.8647785, -7.629112, -21.129435, 5.1048613, -7.5507226, -21.183046, 5.1524935, -7.629112, -21.308428, 5.137542, -5.555945, -21.129435, 5.31504, -5.555945, -21.17429, 5.434868, -6.0741305, -21.129435, 5.431023, -5.555945, -21.129435, 5.31504, -5.0537796, -21.129435, 5.3940706, -5.555945, -21.17429, 5.434868, -5.555945, -21.129435, 5.31504, -3.54707, -21.129435, 5.271039, -5.0537796, -21.129435, 5.3940706, -5.555945, -21.129435, 5.31504, -3.54707, -21.129435, 4.8647785, -3.54707, -21.129435, 5.271039, -3.54707, -21.129435, 4.8647785, -5.555945, -21.129435, 5.31504, -5.555945, -21.129435, 4.9555573, -6.0741305, -21.129435, 5.431023, -5.555945, -21.129435, 4.9555573, -5.555945, -21.129435, 5.31504, -5.555945, -21.129435, 4.9555573, -7.629112, -21.129435, 5.1048613, -7.629112, -21.129435, 4.8647785, -5.555945, -21.129435, 4.9555573, -7.2726197, -21.129435, 5.1539884, -7.629112, -21.129435, 5.1048613, -6.0741305, -21.129435, 5.431023, -7.2726197, -21.129435, 5.1539884, -5.555945, -21.129435, 4.9555573, -7.5507226, -21.183046, 5.1524935, -7.629112, -21.129435, 5.1048613, -7.2726197, -21.129435, 5.1539884, -7.629112, -21.129435, 5.240281, -7.5507226, -21.183046, 5.1524935, -7.2726197, -21.129435, 5.1539884, -7.2726197, -21.129435, 5.1539884, -6.0741305, -21.129435, 5.431023, -7.629112, -21.129435, 5.240281, -7.629112, -21.129435, 5.240281, -6.0741305, -21.129435, 5.431023, -7.629112, -21.129435, 5.4197025, -7.629112, -21.308428, 4.864779, -7.629112, -21.129435, 5.1048613, -7.629112, -21.308428, 5.137542, -7.629112, -21.308428, 4.864779, -7.629112, -21.129435, 4.8647785, -7.629112, -21.129435, 5.1048613, -7.629112, -21.129435, 5.240281, -7.629112, -21.308428, 5.4197025, -7.629112, -21.308428, 5.137542, -7.629112, -21.129435, 5.240281, -7.629112, -21.129435, 5.4197025, -7.629112, -21.308428, 5.4197025, -7.5507226, -21.183046, 5.1524935, -7.629112, -21.129435, 5.240281, -7.629112, -21.308428, 5.137542, -7.629112, -21.129435, 5.4197025, -5.555945, -21.308428, 5.434868, -7.629112, -21.308428, 5.4197025, -5.555945, -21.308428, 5.434868, -7.629112, -21.129435, 5.4197025, -6.0741305, -21.129435, 5.431023, -5.555945, -21.308428, 5.434868, -6.0741305, -21.129435, 5.431023, -5.555945, -21.17429, 5.434868, -5.555945, -21.17429, 5.434868, -3.54707, -21.308428, 5.2710395, -5.555945, -21.308428, 5.434868, -5.555945, -21.17429, 5.434868, -3.54707, -21.129435, 5.271039, -3.54707, -21.308428, 5.2710395, -3.54707, -21.129435, 5.271039, -5.555945, -21.17429, 5.434868, -5.0537796, -21.129435, 5.3940706, -6.926807, -21.30843, 1.0420431, -3.4520202, -21.129435, 0.96386683, -6.926807, -21.129435, 1.0420431, -6.926807, -21.30843, 1.0420431, -3.4520202, -21.30843, 0.96386683, -3.4520202, -21.129435, 0.96386683, -7.723949, -21.129435, 1.0386255, -6.926807, -21.30843, 1.0420431, -6.926807, -21.129435, 1.0420431, -7.723949, -21.129435, 1.0386255, -7.723949, -21.30843, 1.0386255, -6.926807, -21.30843, 1.0420431, -6.926807, -21.30843, 1.5358781, -3.4520202, -21.30843, 0.96386683, -6.926807, -21.30843, 1.0420431, -6.926807, -21.30843, 1.5358781, -3.4520202, -21.30843, 1.444886, -3.4520202, -21.30843, 0.96386683, -7.723949, -21.30843, 1.0386255, -6.926807, -21.30843, 1.5358781, -6.926807, -21.30843, 1.0420431, -7.723949, -21.30843, 1.0386255, -7.723949, -21.30843, 1.444886, -6.926807, -21.30843, 1.5358781, -7.723949, -21.30843, 1.0386255, -7.723949, -21.129435, 1.444886, -7.723949, -21.30843, 1.444886, -7.723949, -21.30843, 1.0386255, -7.723949, -21.129435, 1.0386255, -7.723949, -21.129435, 1.444886, -6.926807, -21.129435, 1.4124193, -6.926807, -21.17429, 1.5358781, -7.1260924, -21.129435, 1.5130231, -6.926807, -21.129435, 1.4124193, -6.0581107, -21.129435, 1.5130231, -6.926807, -21.17429, 1.5358781, -6.926807, -21.129435, 1.4124193, -4.1037025, -21.129435, 1.4619737, -6.0581107, -21.129435, 1.5130231, -7.1260924, -21.129435, 1.5130231, -6.926807, -21.129435, 1.0420431, -6.926807, -21.129435, 1.4124193, -7.1260924, -21.129435, 1.5130231, -7.723949, -21.129435, 1.0386255, -6.926807, -21.129435, 1.0420431, -7.723949, -21.129435, 1.0386255, -7.1260924, -21.129435, 1.5130231, -7.723949, -21.129435, 1.444886, -3.4520202, -21.129435, 0.96386683, -6.926807, -21.129435, 1.4124193, -6.926807, -21.129435, 1.0420431, -6.926807, -21.129435, 1.4124193, -3.4520202, -21.129435, 0.96386683, -4.1037025, -21.129435, 1.4619737, -4.1037025, -21.129435, 1.4619737, -3.4520202, -21.129435, 0.96386683, -3.4520202, -21.129435, 1.3246312, -4.1037025, -21.129435, 1.4619737, -3.4520202, -21.129435, 1.3246312, -3.4520202, -21.17429, 1.444886, -7.723949, -21.129435, 1.444886, -6.926807, -21.30843, 1.5358781, -7.723949, -21.30843, 1.444886, -6.926807, -21.30843, 1.5358781, -7.723949, -21.129435, 1.444886, -7.1260924, -21.129435, 1.5130231, -6.926807, -21.30843, 1.5358781, -7.1260924, -21.129435, 1.5130231, -6.926807, -21.17429, 1.5358781, -6.926807, -21.17429, 1.5358781, -3.4520202, -21.30843, 1.444886, -6.926807, -21.30843, 1.5358781, -6.926807, -21.17429, 1.5358781, -4.1037025, -21.129435, 1.4619737, -3.4520202, -21.30843, 1.444886, -3.4520202, -21.30843, 1.444886, -4.1037025, -21.129435, 1.4619737, -3.4520202, -21.17429, 1.444886, -4.1037025, -21.129435, 1.4619737, -6.926807, -21.17429, 1.5358781, -6.0581107, -21.129435, 1.5130231, -3.4520202, -21.17429, 1.444886, -3.4520202, -21.30843, 0.96386683, -3.4520202, -21.30843, 1.444886, -3.4520202, -21.30843, 0.96386683, -3.4520202, -21.17429, 1.444886, -3.4520202, -21.129435, 1.3246312, -3.4520202, -21.30843, 0.96386683, -3.4520202, -21.129435, 1.3246312, -3.4520202, -21.129435, 0.96386683, -6.9564967, -21.308428, 4.294263, -3.4046016, -21.129435, 4.2160864, -6.9564967, -21.129435, 4.294263, -6.9564967, -21.308428, 4.294263, -3.4046016, -21.308428, 4.2160864, -3.4046016, -21.129435, 4.2160864, -7.7713675, -21.129435, 4.290845, -6.9564967, -21.308428, 4.294263, -6.9564967, -21.129435, 4.294263, -7.7713675, -21.129435, 4.290845, -7.7713675, -21.308428, 4.2908454, -6.9564967, -21.308428, 4.294263, -6.9564967, -21.308428, 4.787884, -3.4046016, -21.308428, 4.2160864, -6.9564967, -21.308428, 4.294263, -6.9564967, -21.308428, 4.787884, -3.4046016, -21.308428, 4.6971054, -3.4046016, -21.308428, 4.2160864, -7.7713675, -21.308428, 4.2908454, -6.9564967, -21.308428, 4.787884, -6.9564967, -21.308428, 4.294263, -7.7713675, -21.308428, 4.2908454, -7.7713675, -21.308428, 4.6971054, -6.9564967, -21.308428, 4.787884, -7.7713675, -21.308428, 4.2908454, -7.7713675, -21.129435, 4.6971054, -7.7713675, -21.308428, 4.6971054, -7.7713675, -21.308428, 4.2908454, -7.7713675, -21.129435, 4.290845, -7.7713675, -21.129435, 4.6971054, -6.9564967, -21.129435, 4.6646385, -6.9564967, -21.17429, 4.7878838, -7.1602683, -21.129435, 4.7652426, -6.9564967, -21.129435, 4.6646385, -6.0685763, -21.129435, 4.7652426, -6.9564967, -21.17429, 4.7878838, -6.9564967, -21.129435, 4.6646385, -4.0705953, -21.129435, 4.7141933, -6.0685763, -21.129435, 4.7652426, -7.1602683, -21.129435, 4.7652426, -6.9564967, -21.129435, 4.294263, -6.9564967, -21.129435, 4.6646385, -7.1602683, -21.129435, 4.7652426, -7.7713675, -21.129435, 4.290845, -6.9564967, -21.129435, 4.294263, -7.7713675, -21.129435, 4.290845, -7.1602683, -21.129435, 4.7652426, -7.7713675, -21.129435, 4.6971054, -3.4046016, -21.129435, 4.2160864, -6.9564967, -21.129435, 4.6646385, -6.9564967, -21.129435, 4.294263, -6.9564967, -21.129435, 4.6646385, -3.4046016, -21.129435, 4.2160864, -4.0705953, -21.129435, 4.7141933, -4.0705953, -21.129435, 4.7141933, -3.4046016, -21.129435, 4.2160864, -3.4046016, -21.129435, 4.576851, -4.0705953, -21.129435, 4.7141933, -3.4046016, -21.129435, 4.576851, -3.4046016, -21.17429, 4.6971054, -7.7713675, -21.129435, 4.6971054, -6.9564967, -21.308428, 4.787884, -7.7713675, -21.308428, 4.6971054, -6.9564967, -21.308428, 4.787884, -7.7713675, -21.129435, 4.6971054, -7.1602683, -21.129435, 4.7652426, -6.9564967, -21.308428, 4.787884, -7.1602683, -21.129435, 4.7652426, -6.9564967, -21.17429, 4.7878838, -6.9564967, -21.17429, 4.7878838, -3.4046016, -21.308428, 4.6971054, -6.9564967, -21.308428, 4.787884, -6.9564967, -21.17429, 4.7878838, -4.0705953, -21.129435, 4.7141933, -3.4046016, -21.308428, 4.6971054, -3.4046016, -21.308428, 4.6971054, -4.0705953, -21.129435, 4.7141933, -3.4046016, -21.17429, 4.6971054, -4.0705953, -21.129435, 4.7141933, -6.9564967, -21.17429, 4.7878838, -6.0685763, -21.129435, 4.7652426, -3.4046016, -21.17429, 4.6971054, -3.4046016, -21.308428, 4.2160864, -3.4046016, -21.308428, 4.6971054, -3.4046016, -21.308428, 4.2160864, -3.4046016, -21.17429, 4.6971054, -3.4046016, -21.129435, 4.576851, -3.4046016, -21.308428, 4.2160864, -3.4046016, -21.129435, 4.576851, -3.4046016, -21.129435, 4.2160864, -3.8851933, -21.397497, 5.744369, -3.5280604, -19.082966, 5.744368, -3.5280604, -21.397497, 5.744369, -3.8851933, -21.397497, 5.744369, -3.8851933, -19.351458, 5.7443686, -3.5280604, -19.082966, 5.744368, -3.8851933, -19.351458, 5.7443686, -3.5280604, -19.082966, 5.387021, -3.5280604, -19.082966, 5.744368, -3.8851933, -19.351458, 5.7443686, -3.8851933, -19.082966, 5.387021, -3.5280604, -19.082966, 5.387021, -3.8851933, -19.082966, 5.387021, -3.5280604, -21.397497, 5.387022, -3.5280604, -19.082966, 5.387021, -3.8851933, -19.082966, 5.387021, -3.8851933, -21.397497, 5.387022, -3.5280604, -21.397497, 5.387022, -3.8851933, -21.397497, 5.387022, -3.5280604, -21.397497, 5.744369, -3.5280604, -21.397497, 5.387022, -3.8851933, -21.397497, 5.387022, -3.8851933, -21.397497, 5.744369, -3.5280604, -21.397497, 5.744369, -3.5280604, -21.397497, 5.744369, -3.5280604, -19.082966, 5.387021, -3.5280604, -21.397497, 5.387022, -3.5280604, -21.397497, 5.744369, -3.5280604, -19.082966, 5.744368, -3.5280604, -19.082966, 5.387021, -3.8851933, -21.397497, 5.387022, -3.8851933, -19.351458, 5.7443686, -3.8851933, -21.397497, 5.744369, -3.8851933, -21.397497, 5.387022, -3.8851933, -19.082966, 5.387021, -3.8851933, -19.351458, 5.7443686, -3.8144932, -21.40476, 5.450674, -3.7010732, -21.359264, 5.450674, -3.7010732, -21.40476, 5.450674, -3.8144932, -21.40476, 5.450674, -3.8144932, -21.359264, 5.450674, -3.7010732, -21.359264, 5.450674, -3.8144932, -21.359264, 5.450674, -3.7010732, -21.359266, -1.0281336, -3.7010732, -21.359264, 5.450674, -3.8144932, -21.359264, 5.450674, -3.8144932, -21.359266, -1.0281336, -3.7010732, -21.359266, -1.0281336, -3.8144932, -21.359266, -1.0281336, -3.7010732, -21.404762, -1.0281336, -3.7010732, -21.359266, -1.0281336, -3.8144932, -21.359266, -1.0281336, -3.8144932, -21.404762, -1.0281336, -3.7010732, -21.404762, -1.0281336, -3.8144932, -21.404762, -1.0281336, -3.7010732, -21.40476, 5.450674, -3.7010732, -21.404762, -1.0281336, -3.8144932, -21.404762, -1.0281336, -3.8144932, -21.40476, 5.450674, -3.7010732, -21.40476, 5.450674, -3.7010732, -21.40476, 5.450674, -3.7010732, -21.359266, -1.0281336, -3.7010732, -21.404762, -1.0281336, -3.7010732, -21.40476, 5.450674, -3.7010732, -21.359264, 5.450674, -3.7010732, -21.359266, -1.0281336, -3.8144932, -21.404762, -1.0281336, -3.8144932, -21.359264, 5.450674, -3.8144932, -21.40476, 5.450674, -3.8144932, -21.404762, -1.0281336, -3.8144932, -21.359266, -1.0281336, -3.8144932, -21.359264, 5.450674, -7.4172244, -21.333633, 0.22268713, -7.393088, -21.35008, -0.25406027, -7.393088, -21.355635, 0.23059022, -7.4172244, -21.333633, 0.22268713, -7.4172244, -21.328295, -0.24551606, -7.393088, -21.35008, -0.25406027, -7.4172244, -21.328295, -0.24551606, -7.393088, -21.208893, -0.27221608, -7.393088, -21.35008, -0.25406027, -7.4172244, -21.328295, -0.24551606, -7.4172244, -21.21466, -0.25277877, -7.393088, -21.208893, -0.27221608, -7.393088, -21.355635, 0.23059022, -7.4172244, -21.371868, -0.26239038, -7.4172244, -21.377848, 0.2384932, -7.393088, -21.355635, 0.23059022, -7.393088, -21.35008, -0.25406027, -7.4172244, -21.371868, -0.26239038, -7.393088, -21.35008, -0.25406027, -7.4172244, -21.20334, -0.29122615, -7.4172244, -21.371868, -0.26239038, -7.393088, -21.35008, -0.25406027, -7.393088, -21.208893, -0.27221608, -7.4172244, -21.20334, -0.29122615, -7.4172244, -21.377848, 0.2384932, -7.441361, -21.35008, -0.25406027, -7.441361, -21.355635, 0.23059022, -7.4172244, -21.377848, 0.2384932, -7.4172244, -21.371868, -0.26239038, -7.441361, -21.35008, -0.25406027, -7.4172244, -21.371868, -0.26239038, -7.441361, -21.208893, -0.27221608, -7.441361, -21.35008, -0.25406027, -7.4172244, -21.371868, -0.26239038, -7.4172244, -21.20334, -0.29122615, -7.441361, -21.208893, -0.27221608, -7.441361, -21.355635, 0.23059022, -7.4172244, -21.328295, -0.24551606, -7.4172244, -21.333633, 0.22268713, -7.441361, -21.355635, 0.23059022, -7.441361, -21.35008, -0.25406027, -7.4172244, -21.328295, -0.24551606, -7.441361, -21.35008, -0.25406027, -7.4172244, -21.21466, -0.25277877, -7.4172244, -21.328295, -0.24551606, -7.441361, -21.35008, -0.25406027, -7.441361, -21.208893, -0.27221608, -7.4172244, -21.21466, -0.25277877, -7.4172244, -21.21466, -0.25277877, -7.393088, -21.090775, -0.24231243, -7.393088, -21.208893, -0.27221608, -7.4172244, -21.21466, -0.25277877, -7.4172244, -21.114271, -0.23804045, -7.393088, -21.090775, -0.24231243, -7.4172244, -21.114271, -0.23804045, -7.393088, -21.088425, 0.22674549, -7.393088, -21.090775, -0.24231243, -7.4172244, -21.114271, -0.23804045, -7.4172244, -21.111708, 0.22204626, -7.393088, -21.088425, 0.22674549, -7.4172244, -21.111708, 0.22204626, -7.393088, -21.219574, 0.2705326, -7.393088, -21.088425, 0.22674549, -7.4172244, -21.111708, 0.22204626, -7.4172244, -21.224485, 0.2510954, -7.393088, -21.219574, 0.2705326, -7.393088, -21.208893, -0.27221608, -7.4172244, -21.06728, -0.24658465, -7.4172244, -21.20334, -0.29122615, -7.393088, -21.208893, -0.27221608, -7.393088, -21.090775, -0.24231243, -7.4172244, -21.06728, -0.24658465, -7.393088, -21.090775, -0.24231243, -7.4172244, -21.06493, 0.23144436, -7.4172244, -21.06728, -0.24658465, -7.393088, -21.090775, -0.24231243, -7.393088, -21.088425, 0.22674549, -7.4172244, -21.06493, 0.23144436, -7.393088, -21.088425, 0.22674549, -7.4172244, -21.214874, 0.28996992, -7.4172244, -21.06493, 0.23144436, -7.393088, -21.088425, 0.22674549, -7.393088, -21.219574, 0.2705326, -7.4172244, -21.214874, 0.28996992, -7.4172244, -21.20334, -0.29122615, -7.441361, -21.090775, -0.24231243, -7.441361, -21.208893, -0.27221608, -7.4172244, -21.20334, -0.29122615, -7.4172244, -21.06728, -0.24658465, -7.441361, -21.090775, -0.24231243, -7.441361, -21.208893, -0.27221608, -7.4172244, -21.114271, -0.23804045, -7.4172244, -21.21466, -0.25277877, -7.441361, -21.208893, -0.27221608, -7.441361, -21.090775, -0.24231243, -7.4172244, -21.114271, -0.23804045, -7.441361, -21.090775, -0.24231243, -7.4172244, -21.111708, 0.22204626, -7.4172244, -21.114271, -0.23804045, -7.441361, -21.090775, -0.24231243, -7.441361, -21.088425, 0.22674549, -7.4172244, -21.111708, 0.22204626, -7.441361, -21.088425, 0.22674549, -7.4172244, -21.224485, 0.2510954, -7.4172244, -21.111708, 0.22204626, -7.441361, -21.088425, 0.22674549, -7.441361, -21.219574, 0.2705326, -7.4172244, -21.224485, 0.2510954, -7.4172244, -21.06728, -0.24658465, -7.441361, -21.088425, 0.22674549, -7.441361, -21.090775, -0.24231243, -7.4172244, -21.06728, -0.24658465, -7.4172244, -21.06493, 0.23144436, -7.441361, -21.088425, 0.22674549, -7.4172244, -21.06493, 0.23144436, -7.441361, -21.219574, 0.2705326, -7.441361, -21.088425, 0.22674549, -7.4172244, -21.06493, 0.23144436, -7.4172244, -21.214874, 0.28996992, -7.441361, -21.219574, 0.2705326, -7.4172244, -21.224485, 0.2510954, -7.393088, -21.355635, 0.23059022, -7.393088, -21.219574, 0.2705326, -7.4172244, -21.224485, 0.2510954, -7.4172244, -21.333633, 0.22268713, -7.393088, -21.355635, 0.23059022, -7.393088, -21.219574, 0.2705326, -7.4172244, -21.377848, 0.2384932, -7.4172244, -21.214874, 0.28996992, -7.393088, -21.219574, 0.2705326, -7.393088, -21.355635, 0.23059022, -7.4172244, -21.377848, 0.2384932, -7.4172244, -21.214874, 0.28996992, -7.441361, -21.355635, 0.23059022, -7.441361, -21.219574, 0.2705326, -7.4172244, -21.214874, 0.28996992, -7.4172244, -21.377848, 0.2384932, -7.441361, -21.355635, 0.23059022, -7.441361, -21.219574, 0.2705326, -7.4172244, -21.333633, 0.22268713, -7.4172244, -21.224485, 0.2510954, -7.441361, -21.219574, 0.2705326, -7.441361, -21.355635, 0.23059022, -7.4172244, -21.333633, 0.22268713, -3.8775039, -21.320818, 0.20923042, -3.8561444, -21.335342, -0.30446887, -3.8561444, -21.34047, 0.21777427, -3.8775039, -21.320818, 0.20923042, -3.8775039, -21.316118, -0.29528427, -3.8561444, -21.335342, -0.30446887, -3.8775039, -21.316118, -0.29528427, -3.8561444, -21.210388, -0.32390642, -3.8561444, -21.335342, -0.30446887, -3.8775039, -21.316118, -0.29528427, -3.8775039, -21.215515, -0.3031876, -3.8561444, -21.210388, -0.32390642, -3.8561444, -21.34047, 0.21777427, -3.8775039, -21.35478, -0.31322622, -3.8775039, -21.36012, 0.22631812, -3.8561444, -21.34047, 0.21777427, -3.8561444, -21.335342, -0.30446887, -3.8775039, -21.35478, -0.31322622, -3.8561444, -21.335342, -0.30446887, -3.8775039, -21.205263, -0.34462523, -3.8775039, -21.35478, -0.31322622, -3.8561444, -21.335342, -0.30446887, -3.8561444, -21.210388, -0.32390642, -3.8775039, -21.205263, -0.34462523, -3.8775039, -21.36012, 0.22631812, -3.8988638, -21.335342, -0.30446887, -3.8988638, -21.34047, 0.21777427, -3.8775039, -21.36012, 0.22631812, -3.8775039, -21.35478, -0.31322622, -3.8988638, -21.335342, -0.30446887, -3.8775039, -21.35478, -0.31322622, -3.8988638, -21.210388, -0.32390642, -3.8988638, -21.335342, -0.30446887, -3.8775039, -21.35478, -0.31322622, -3.8775039, -21.205263, -0.34462523, -3.8988638, -21.210388, -0.32390642, -3.8988638, -21.34047, 0.21777427, -3.8775039, -21.316118, -0.29528427, -3.8775039, -21.320818, 0.20923042, -3.8988638, -21.34047, 0.21777427, -3.8988638, -21.335342, -0.30446887, -3.8775039, -21.316118, -0.29528427, -3.8988638, -21.335342, -0.30446887, -3.8775039, -21.215515, -0.3031876, -3.8775039, -21.316118, -0.29528427, -3.8988638, -21.335342, -0.30446887, -3.8988638, -21.210388, -0.32390642, -3.8775039, -21.215515, -0.3031876, -3.8775039, -21.215515, -0.3031876, -3.8561444, -21.105513, -0.29165316, -3.8561444, -21.210388, -0.32390642, -3.8775039, -21.215515, -0.3031876, -3.8775039, -21.126446, -0.2871678, -3.8561444, -21.105513, -0.29165316, -3.8775039, -21.126446, -0.2871678, -3.8561444, -21.10359, 0.2135024, -3.8561444, -21.105513, -0.29165316, -3.8775039, -21.126446, -0.2871678, -3.8775039, -21.12431, 0.20858967, -3.8561444, -21.10359, 0.2135024, -3.8775039, -21.12431, 0.20858967, -3.8561444, -21.219788, 0.26070714, -3.8561444, -21.10359, 0.2135024, -3.8775039, -21.12431, 0.20858967, -3.8775039, -21.224058, 0.23977482, -3.8561444, -21.219788, 0.26070714, -3.8561444, -21.210388, -0.32390642, -3.8775039, -21.084795, -0.2963524, -3.8775039, -21.205263, -0.34462523, -3.8561444, -21.210388, -0.32390642, -3.8561444, -21.105513, -0.29165316, -3.8775039, -21.084795, -0.2963524, -3.8561444, -21.105513, -0.29165316, -3.8775039, -21.082659, 0.21862864, -3.8775039, -21.084795, -0.2963524, -3.8561444, -21.105513, -0.29165316, -3.8561444, -21.10359, 0.2135024, -3.8775039, -21.082659, 0.21862864, -3.8561444, -21.10359, 0.2135024, -3.8775039, -21.215515, 0.2816397, -3.8775039, -21.082659, 0.21862864, -3.8561444, -21.10359, 0.2135024, -3.8561444, -21.219788, 0.26070714, -3.8775039, -21.215515, 0.2816397, -3.8775039, -21.205263, -0.34462523, -3.8988638, -21.105513, -0.29165316, -3.8988638, -21.210388, -0.32390642, -3.8775039, -21.205263, -0.34462523, -3.8775039, -21.084795, -0.2963524, -3.8988638, -21.105513, -0.29165316, -3.8988638, -21.210388, -0.32390642, -3.8775039, -21.126446, -0.2871678, -3.8775039, -21.215515, -0.3031876, -3.8988638, -21.210388, -0.32390642, -3.8988638, -21.105513, -0.29165316, -3.8775039, -21.126446, -0.2871678, -3.8988638, -21.105513, -0.29165316, -3.8775039, -21.12431, 0.20858967, -3.8775039, -21.126446, -0.2871678, -3.8988638, -21.105513, -0.29165316, -3.8988638, -21.10359, 0.2135024, -3.8775039, -21.12431, 0.20858967, -3.8988638, -21.10359, 0.2135024, -3.8775039, -21.224058, 0.23977482, -3.8775039, -21.12431, 0.20858967, -3.8988638, -21.10359, 0.2135024, -3.8988638, -21.219788, 0.26070714, -3.8775039, -21.224058, 0.23977482, -3.8775039, -21.084795, -0.2963524, -3.8988638, -21.10359, 0.2135024, -3.8988638, -21.105513, -0.29165316, -3.8775039, -21.084795, -0.2963524, -3.8775039, -21.082659, 0.21862864, -3.8988638, -21.10359, 0.2135024, -3.8775039, -21.082659, 0.21862864, -3.8988638, -21.219788, 0.26070714, -3.8988638, -21.10359, 0.2135024, -3.8775039, -21.082659, 0.21862864, -3.8775039, -21.215515, 0.2816397, -3.8988638, -21.219788, 0.26070714, -3.8775039, -21.224058, 0.23977482, -3.8561444, -21.34047, 0.21777427, -3.8561444, -21.219788, 0.26070714, -3.8775039, -21.224058, 0.23977482, -3.8775039, -21.320818, 0.20923042, -3.8561444, -21.34047, 0.21777427, -3.8561444, -21.219788, 0.26070714, -3.8775039, -21.36012, 0.22631812, -3.8775039, -21.215515, 0.2816397, -3.8561444, -21.219788, 0.26070714, -3.8561444, -21.34047, 0.21777427, -3.8775039, -21.36012, 0.22631812, -3.8775039, -21.215515, 0.2816397, -3.8988638, -21.34047, 0.21777427, -3.8988638, -21.219788, 0.26070714, -3.8775039, -21.215515, 0.2816397, -3.8775039, -21.36012, 0.22631812, -3.8988638, -21.34047, 0.21777427, -3.8988638, -21.219788, 0.26070714, -3.8775039, -21.320818, 0.20923042, -3.8775039, -21.224058, 0.23977482, -3.8988638, -21.219788, 0.26070714, -3.8988638, -21.34047, 0.21777427, -3.8775039, -21.320818, 0.20923042, -7.34631, -21.333633, 0.9091861, -7.3221745, -21.35008, 0.3158151, -7.3221745, -21.355635, 0.9190115, -7.34631, -21.333633, 0.9091861, -7.34631, -21.328295, 0.3260677, -7.3221745, -21.35008, 0.3158151, -7.34631, -21.328295, 0.3260677, -7.3221745, -21.208893, 0.2931739, -7.3221745, -21.35008, 0.3158151, -7.34631, -21.328295, 0.3260677, -7.34631, -21.21466, 0.3170967, -7.3221745, -21.208893, 0.2931739, -7.3221745, -21.355635, 0.9190115, -7.34631, -21.371868, 0.30534887, -7.34631, -21.377848, 0.92883694, -7.3221745, -21.355635, 0.9190115, -7.3221745, -21.35008, 0.3158151, -7.34631, -21.371868, 0.30534887, -7.3221745, -21.35008, 0.3158151, -7.34631, -21.20334, 0.2692511, -7.34631, -21.371868, 0.30534887, -7.3221745, -21.35008, 0.3158151, -7.3221745, -21.208893, 0.2931739, -7.34631, -21.20334, 0.2692511, -7.34631, -21.377848, 0.92883694, -7.3704467, -21.35008, 0.3158151, -7.3704467, -21.355635, 0.9190115, -7.34631, -21.377848, 0.92883694, -7.34631, -21.371868, 0.30534887, -7.3704467, -21.35008, 0.3158151, -7.34631, -21.371868, 0.30534887, -7.3704467, -21.208893, 0.2931739, -7.3704467, -21.35008, 0.3158151, -7.34631, -21.371868, 0.30534887, -7.34631, -21.20334, 0.2692511, -7.3704467, -21.208893, 0.2931739, -7.3704467, -21.355635, 0.9190115, -7.34631, -21.328295, 0.3260677, -7.34631, -21.333633, 0.9091861, -7.3704467, -21.355635, 0.9190115, -7.3704467, -21.35008, 0.3158151, -7.34631, -21.328295, 0.3260677, -7.3704467, -21.35008, 0.3158151, -7.34631, -21.21466, 0.3170967, -7.34631, -21.328295, 0.3260677, -7.3704467, -21.35008, 0.3158151, -7.3704467, -21.208893, 0.2931739, -7.34631, -21.21466, 0.3170967, -7.34631, -21.21466, 0.3170967, -7.3221745, -21.090775, 0.33033967, -7.3221745, -21.208893, 0.2931739, -7.34631, -21.21466, 0.3170967, -7.34631, -21.114271, 0.33567965, -7.3221745, -21.090775, 0.33033967, -7.34631, -21.114271, 0.33567965, -7.3221745, -21.088425, 0.91409886, -7.3221745, -21.090775, 0.33033967, -7.34631, -21.114271, 0.33567965, -7.34631, -21.111708, 0.90833163, -7.3221745, -21.088425, 0.91409886, -7.34631, -21.111708, 0.90833163, -7.3221745, -21.219574, 0.96877944, -7.3221745, -21.088425, 0.91409886, -7.34631, -21.111708, 0.90833163, -7.34631, -21.224485, 0.9444295, -7.3221745, -21.219574, 0.96877944, -7.3221745, -21.208893, 0.2931739, -7.34631, -21.06728, 0.32499957, -7.34631, -21.20334, 0.2692511, -7.3221745, -21.208893, 0.2931739, -7.3221745, -21.090775, 0.33033967, -7.34631, -21.06728, 0.32499957, -7.3221745, -21.090775, 0.33033967, -7.34631, -21.06493, 0.92007935, -7.34631, -21.06728, 0.32499957, -7.3221745, -21.090775, 0.33033967, -7.3221745, -21.088425, 0.91409886, -7.34631, -21.06493, 0.92007935, -7.3221745, -21.088425, 0.91409886, -7.34631, -21.214874, 0.99291587, -7.34631, -21.06493, 0.92007935, -7.3221745, -21.088425, 0.91409886, -7.3221745, -21.219574, 0.96877944, -7.34631, -21.214874, 0.99291587, -7.34631, -21.20334, 0.2692511, -7.3704467, -21.090775, 0.33033967, -7.3704467, -21.208893, 0.2931739, -7.34631, -21.20334, 0.2692511, -7.34631, -21.06728, 0.32499957, -7.3704467, -21.090775, 0.33033967, -7.3704467, -21.208893, 0.2931739, -7.34631, -21.114271, 0.33567965, -7.34631, -21.21466, 0.3170967, -7.3704467, -21.208893, 0.2931739, -7.3704467, -21.090775, 0.33033967, -7.34631, -21.114271, 0.33567965, -7.3704467, -21.090775, 0.33033967, -7.34631, -21.111708, 0.90833163, -7.34631, -21.114271, 0.33567965, -7.3704467, -21.090775, 0.33033967, -7.3704467, -21.088425, 0.91409886, -7.34631, -21.111708, 0.90833163, -7.3704467, -21.088425, 0.91409886, -7.34631, -21.224485, 0.9444295, -7.34631, -21.111708, 0.90833163, -7.3704467, -21.088425, 0.91409886, -7.3704467, -21.219574, 0.96877944, -7.34631, -21.224485, 0.9444295, -7.34631, -21.06728, 0.32499957, -7.3704467, -21.088425, 0.91409886, -7.3704467, -21.090775, 0.33033967, -7.34631, -21.06728, 0.32499957, -7.34631, -21.06493, 0.92007935, -7.3704467, -21.088425, 0.91409886, -7.34631, -21.06493, 0.92007935, -7.3704467, -21.219574, 0.96877944, -7.3704467, -21.088425, 0.91409886, -7.34631, -21.06493, 0.92007935, -7.34631, -21.214874, 0.99291587, -7.3704467, -21.219574, 0.96877944, -7.34631, -21.224485, 0.9444295, -7.3221745, -21.355635, 0.9190115, -7.3221745, -21.219574, 0.96877944, -7.34631, -21.224485, 0.9444295, -7.34631, -21.333633, 0.9091861, -7.3221745, -21.355635, 0.9190115, -7.3221745, -21.219574, 0.96877944, -7.34631, -21.377848, 0.92883694, -7.34631, -21.214874, 0.99291587, -7.3221745, -21.219574, 0.96877944, -7.3221745, -21.355635, 0.9190115, -7.34631, -21.377848, 0.92883694, -7.34631, -21.214874, 0.99291587, -7.3704467, -21.355635, 0.9190115, -7.3704467, -21.219574, 0.96877944, -7.34631, -21.214874, 0.99291587, -7.34631, -21.377848, 0.92883694, -7.3704467, -21.355635, 0.9190115, -7.3704467, -21.219574, 0.96877944, -7.34631, -21.333633, 0.9091861, -7.34631, -21.224485, 0.9444295, -7.3704467, -21.219574, 0.96877944, -7.3704467, -21.355635, 0.9190115, -7.34631, -21.333633, 0.9091861, -7.433244, -21.333633, 2.1138701, -7.4093213, -21.35008, 1.6759974, -7.4093213, -21.355633, 2.1211324, -7.433244, -21.333633, 2.1138701, -7.433244, -21.328293, 1.6839005, -7.4093213, -21.35008, 1.6759974, -7.4093213, -21.355633, 2.1211324, -7.433244, -21.371868, 1.6683079, -7.433244, -21.377848, 2.1283946, -7.4093213, -21.355633, 2.1211324, -7.4093213, -21.35008, 1.6759974, -7.433244, -21.371868, 1.6683079, -7.433244, -21.377848, 2.1283946, -7.457381, -21.35008, 1.6759974, -7.457381, -21.355633, 2.1211324, -7.433244, -21.377848, 2.1283946, -7.433244, -21.371868, 1.6683079, -7.457381, -21.35008, 1.6759974, -7.457381, -21.355633, 2.1211324, -7.433244, -21.328293, 1.6839005, -7.433244, -21.333633, 2.1138701, -7.457381, -21.355633, 2.1211324, -7.457381, -21.35008, 1.6759974, -7.433244, -21.328293, 1.6839005, -7.433244, -21.328293, 1.6839005, -7.4093213, -21.208893, 1.6595504, -7.4093213, -21.35008, 1.6759974, -7.433244, -21.328293, 1.6839005, -7.433244, -21.21466, 1.6770654, -7.4093213, -21.208893, 1.6595504, -7.4093213, -21.35008, 1.6759974, -7.433244, -21.203339, 1.6418219, -7.433244, -21.371868, 1.6683079, -7.4093213, -21.35008, 1.6759974, -7.4093213, -21.208893, 1.6595504, -7.433244, -21.203339, 1.6418219, -7.433244, -21.371868, 1.6683079, -7.457381, -21.208893, 1.6595504, -7.457381, -21.35008, 1.6759974, -7.433244, -21.371868, 1.6683079, -7.433244, -21.203339, 1.6418219, -7.457381, -21.208893, 1.6595504, -7.457381, -21.35008, 1.6759974, -7.433244, -21.21466, 1.6770654, -7.433244, -21.328293, 1.6839005, -7.457381, -21.35008, 1.6759974, -7.457381, -21.208893, 1.6595504, -7.433244, -21.21466, 1.6770654, -7.433244, -21.21466, 1.6770654, -7.4093213, -21.090775, 1.6868907, -7.4093213, -21.208893, 1.6595504, -7.433244, -21.21466, 1.6770654, -7.433244, -21.11427, 1.6907355, -7.4093213, -21.090775, 1.6868907, -7.4093213, -21.208893, 1.6595504, -7.433244, -21.067278, 1.6828324, -7.433244, -21.203339, 1.6418219, -7.4093213, -21.208893, 1.6595504, -7.4093213, -21.090775, 1.6868907, -7.433244, -21.067278, 1.6828324, -7.433244, -21.203339, 1.6418219, -7.457381, -21.090775, 1.6868907, -7.457381, -21.208893, 1.6595504, -7.433244, -21.203339, 1.6418219, -7.433244, -21.067278, 1.6828324, -7.457381, -21.090775, 1.6868907, -7.457381, -21.208893, 1.6595504, -7.433244, -21.11427, 1.6907355, -7.433244, -21.21466, 1.6770654, -7.457381, -21.208893, 1.6595504, -7.457381, -21.090775, 1.6868907, -7.433244, -21.11427, 1.6907355, -7.433244, -21.11427, 1.6907355, -7.4093213, -21.088425, 2.1175013, -7.4093213, -21.090775, 1.6868907, -7.433244, -21.11427, 1.6907355, -7.433244, -21.111706, 2.1132293, -7.4093213, -21.088425, 2.1175013, -7.433244, -21.111706, 2.1132293, -7.4093213, -21.219572, 2.157871, -7.4093213, -21.088425, 2.1175013, -7.433244, -21.111706, 2.1132293, -7.433244, -21.224485, 2.1399288, -7.4093213, -21.219572, 2.157871, -7.4093213, -21.090775, 1.6868907, -7.433244, -21.064928, 2.121773, -7.433244, -21.067278, 1.6828324, -7.4093213, -21.090775, 1.6868907, -7.4093213, -21.088425, 2.1175013, -7.433244, -21.064928, 2.121773, -7.4093213, -21.088425, 2.1175013, -7.433244, -21.214874, 2.1755996, -7.433244, -21.064928, 2.121773, -7.4093213, -21.088425, 2.1175013, -7.4093213, -21.219572, 2.157871, -7.433244, -21.214874, 2.1755996, -7.433244, -21.067278, 1.6828324, -7.457381, -21.088425, 2.1175013, -7.457381, -21.090775, 1.6868907, -7.433244, -21.067278, 1.6828324, -7.433244, -21.064928, 2.121773, -7.457381, -21.088425, 2.1175013, -7.433244, -21.064928, 2.121773, -7.457381, -21.219572, 2.157871, -7.457381, -21.088425, 2.1175013, -7.433244, -21.064928, 2.121773, -7.433244, -21.214874, 2.1755996, -7.457381, -21.219572, 2.157871, -7.457381, -21.090775, 1.6868907, -7.433244, -21.111706, 2.1132293, -7.433244, -21.11427, 1.6907355, -7.457381, -21.090775, 1.6868907, -7.457381, -21.088425, 2.1175013, -7.433244, -21.111706, 2.1132293, -7.457381, -21.088425, 2.1175013, -7.433244, -21.224485, 2.1399288, -7.433244, -21.111706, 2.1132293, -7.457381, -21.088425, 2.1175013, -7.457381, -21.219572, 2.157871, -7.433244, -21.224485, 2.1399288, -7.433244, -21.224485, 2.1399288, -7.4093213, -21.355633, 2.1211324, -7.4093213, -21.219572, 2.157871, -7.433244, -21.224485, 2.1399288, -7.433244, -21.333633, 2.1138701, -7.4093213, -21.355633, 2.1211324, -7.4093213, -21.219572, 2.157871, -7.433244, -21.377848, 2.1283946, -7.433244, -21.214874, 2.1755996, -7.4093213, -21.219572, 2.157871, -7.4093213, -21.355633, 2.1211324, -7.433244, -21.377848, 2.1283946, -7.433244, -21.214874, 2.1755996, -7.457381, -21.355633, 2.1211324, -7.457381, -21.219572, 2.157871, -7.433244, -21.214874, 2.1755996, -7.433244, -21.377848, 2.1283946, -7.457381, -21.355633, 2.1211324, -7.457381, -21.219572, 2.157871, -7.433244, -21.333633, 2.1138701, -7.433244, -21.224485, 2.1399288, -7.457381, -21.219572, 2.157871, -7.457381, -21.355633, 2.1211324, -7.433244, -21.333633, 2.1138701, -7.433244, -21.333633, 3.4082646, -7.4093213, -21.35008, 2.8360398, -7.4093213, -21.355633, 3.4176629, -7.433244, -21.333633, 3.4082646, -7.433244, -21.328293, 2.8460789, -7.4093213, -21.35008, 2.8360398, -7.433244, -21.328293, 2.8460789, -7.4093213, -21.208893, 2.8142529, -7.4093213, -21.35008, 2.8360398, -7.433244, -21.328293, 2.8460789, -7.433244, -21.21466, 2.837535, -7.4093213, -21.208893, 2.8142529, -7.4093213, -21.355633, 3.4176629, -7.433244, -21.371866, 2.8260007, -7.433244, -21.377848, 3.4272747, -7.4093213, -21.355633, 3.4176629, -7.4093213, -21.35008, 2.8360398, -7.433244, -21.371866, 2.8260007, -7.4093213, -21.35008, 2.8360398, -7.433244, -21.203339, 2.7911844, -7.433244, -21.371866, 2.8260007, -7.4093213, -21.35008, 2.8360398, -7.4093213, -21.208893, 2.8142529, -7.433244, -21.203339, 2.7911844, -7.433244, -21.377848, 3.4272747, -7.457381, -21.35008, 2.8360398, -7.457381, -21.355633, 3.4176629, -7.433244, -21.377848, 3.4272747, -7.433244, -21.371866, 2.8260007, -7.457381, -21.35008, 2.8360398, -7.433244, -21.371866, 2.8260007, -7.457381, -21.208893, 2.8142529, -7.457381, -21.35008, 2.8360398, -7.433244, -21.371866, 2.8260007, -7.433244, -21.203339, 2.7911844, -7.457381, -21.208893, 2.8142529, -7.457381, -21.355633, 3.4176629, -7.433244, -21.328293, 2.8460789, -7.433244, -21.333633, 3.4082646, -7.457381, -21.355633, 3.4176629, -7.457381, -21.35008, 2.8360398, -7.433244, -21.328293, 2.8460789, -7.457381, -21.35008, 2.8360398, -7.433244, -21.21466, 2.837535, -7.433244, -21.328293, 2.8460789, -7.457381, -21.35008, 2.8360398, -7.457381, -21.208893, 2.8142529, -7.433244, -21.21466, 2.837535, -7.433244, -21.21466, 2.837535, -7.4093213, -21.090775, 2.850137, -7.4093213, -21.208893, 2.8142529, -7.433244, -21.21466, 2.837535, -7.433244, -21.11427, 2.8552632, -7.4093213, -21.090775, 2.850137, -7.433244, -21.11427, 2.8552632, -7.4093213, -21.088425, 3.4129639, -7.4093213, -21.090775, 2.850137, -7.433244, -21.11427, 2.8552632, -7.433244, -21.111706, 3.4071965, -7.4093213, -21.088425, 3.4129639, -7.433244, -21.111706, 3.4071965, -7.4093213, -21.219572, 3.4655085, -7.4093213, -21.088425, 3.4129639, -7.433244, -21.111706, 3.4071965, -7.433244, -21.224485, 3.4422264, -7.4093213, -21.219572, 3.4655085, -7.4093213, -21.208893, 2.8142529, -7.433244, -21.067278, 2.8450108, -7.433244, -21.203339, 2.7911844, -7.4093213, -21.208893, 2.8142529, -7.4093213, -21.090775, 2.850137, -7.433244, -21.067278, 2.8450108, -7.4093213, -21.090775, 2.850137, -7.433244, -21.064928, 3.4187307, -7.433244, -21.067278, 2.8450108, -7.4093213, -21.090775, 2.850137, -7.4093213, -21.088425, 3.4129639, -7.433244, -21.064928, 3.4187307, -7.4093213, -21.088425, 3.4129639, -7.433244, -21.214872, 3.4887905, -7.433244, -21.064928, 3.4187307, -7.4093213, -21.088425, 3.4129639, -7.4093213, -21.219572, 3.4655085, -7.433244, -21.214872, 3.4887905, -7.433244, -21.203339, 2.7911844, -7.457381, -21.090775, 2.850137, -7.457381, -21.208893, 2.8142529, -7.433244, -21.203339, 2.7911844, -7.433244, -21.067278, 2.8450108, -7.457381, -21.090775, 2.850137, -7.457381, -21.208893, 2.8142529, -7.433244, -21.11427, 2.8552632, -7.433244, -21.21466, 2.837535, -7.457381, -21.208893, 2.8142529, -7.457381, -21.090775, 2.850137, -7.433244, -21.11427, 2.8552632, -7.457381, -21.090775, 2.850137, -7.433244, -21.111706, 3.4071965, -7.433244, -21.11427, 2.8552632, -7.457381, -21.090775, 2.850137, -7.457381, -21.088425, 3.4129639, -7.433244, -21.111706, 3.4071965, -7.457381, -21.088425, 3.4129639, -7.433244, -21.224485, 3.4422264, -7.433244, -21.111706, 3.4071965, -7.457381, -21.088425, 3.4129639, -7.457381, -21.219572, 3.4655085, -7.433244, -21.224485, 3.4422264, -7.433244, -21.067278, 2.8450108, -7.457381, -21.088425, 3.4129639, -7.457381, -21.090775, 2.850137, -7.433244, -21.067278, 2.8450108, -7.433244, -21.064928, 3.4187307, -7.457381, -21.088425, 3.4129639, -7.433244, -21.064928, 3.4187307, -7.457381, -21.219572, 3.4655085, -7.457381, -21.088425, 3.4129639, -7.433244, -21.064928, 3.4187307, -7.433244, -21.214872, 3.4887905, -7.457381, -21.219572, 3.4655085, -7.433244, -21.224485, 3.4422264, -7.4093213, -21.355633, 3.4176629, -7.4093213, -21.219572, 3.4655085, -7.433244, -21.224485, 3.4422264, -7.433244, -21.333633, 3.4082646, -7.4093213, -21.355633, 3.4176629, -7.4093213, -21.219572, 3.4655085, -7.433244, -21.377848, 3.4272747, -7.433244, -21.214872, 3.4887905, -7.4093213, -21.219572, 3.4655085, -7.4093213, -21.355633, 3.4176629, -7.433244, -21.377848, 3.4272747, -7.433244, -21.214872, 3.4887905, -7.457381, -21.355633, 3.4176629, -7.457381, -21.219572, 3.4655085, -7.433244, -21.214872, 3.4887905, -7.433244, -21.377848, 3.4272747, -7.457381, -21.355633, 3.4176629, -7.457381, -21.219572, 3.4655085, -7.433244, -21.333633, 3.4082646, -7.433244, -21.224485, 3.4422264, -7.457381, -21.219572, 3.4655085, -7.457381, -21.355633, 3.4176629, -7.433244, -21.333633, 3.4082646, -3.7589583, -21.320816, 4.036238, -3.7375984, -21.335342, 3.5225387, -3.7375984, -21.340467, 4.044782, -3.7589583, -21.320816, 4.036238, -3.7589583, -21.316118, 3.5317235, -3.7375984, -21.335342, 3.5225387, -3.7589583, -21.316118, 3.5317235, -3.7375984, -21.210388, 3.5031013, -3.7375984, -21.335342, 3.5225387, -3.7589583, -21.316118, 3.5317235, -3.7589583, -21.215513, 3.5238204, -3.7375984, -21.210388, 3.5031013, -3.7375984, -21.340467, 4.044782, -3.7589583, -21.354778, 3.5137813, -3.7589583, -21.360119, 4.0533257, -3.7375984, -21.340467, 4.044782, -3.7375984, -21.335342, 3.5225387, -3.7589583, -21.354778, 3.5137813, -3.7375984, -21.335342, 3.5225387, -3.7589583, -21.205261, 3.4823828, -3.7589583, -21.354778, 3.5137813, -3.7375984, -21.335342, 3.5225387, -3.7375984, -21.210388, 3.5031013, -3.7589583, -21.205261, 3.4823828, -3.7589583, -21.360119, 4.0533257, -3.7803178, -21.335342, 3.5225387, -3.7803178, -21.340467, 4.044782, -3.7589583, -21.360119, 4.0533257, -3.7589583, -21.354778, 3.5137813, -3.7803178, -21.335342, 3.5225387, -3.7589583, -21.354778, 3.5137813, -3.7803178, -21.210388, 3.5031013, -3.7803178, -21.335342, 3.5225387, -3.7589583, -21.354778, 3.5137813, -3.7589583, -21.205261, 3.4823828, -3.7803178, -21.210388, 3.5031013, -3.7803178, -21.340467, 4.044782, -3.7589583, -21.316118, 3.5317235, -3.7589583, -21.320816, 4.036238, -3.7803178, -21.340467, 4.044782, -3.7803178, -21.335342, 3.5225387, -3.7589583, -21.316118, 3.5317235, -3.7803178, -21.335342, 3.5225387, -3.7589583, -21.215513, 3.5238204, -3.7589583, -21.316118, 3.5317235, -3.7803178, -21.335342, 3.5225387, -3.7803178, -21.210388, 3.5031013, -3.7589583, -21.215513, 3.5238204, -3.7589583, -21.215513, 3.5238204, -3.7375984, -21.105513, 3.5353546, -3.7375984, -21.210388, 3.5031013, -3.7589583, -21.215513, 3.5238204, -3.7589583, -21.126444, 3.5398402, -3.7375984, -21.105513, 3.5353546, -3.7589583, -21.126444, 3.5398402, -3.7375984, -21.10359, 4.04051, -3.7375984, -21.105513, 3.5353546, -3.7589583, -21.126444, 3.5398402, -3.7589583, -21.124308, 4.0355973, -3.7375984, -21.10359, 4.04051, -3.7589583, -21.124308, 4.0355973, -3.7375984, -21.219786, 4.087715, -3.7375984, -21.10359, 4.04051, -3.7589583, -21.124308, 4.0355973, -3.7589583, -21.224058, 4.0667825, -3.7375984, -21.219786, 4.087715, -3.7375984, -21.210388, 3.5031013, -3.7589583, -21.084793, 3.5306554, -3.7589583, -21.205261, 3.4823828, -3.7375984, -21.210388, 3.5031013, -3.7375984, -21.105513, 3.5353546, -3.7589583, -21.084793, 3.5306554, -3.7375984, -21.105513, 3.5353546, -3.7589583, -21.082657, 4.045636, -3.7589583, -21.084793, 3.5306554, -3.7375984, -21.105513, 3.5353546, -3.7375984, -21.10359, 4.04051, -3.7589583, -21.082657, 4.045636, -3.7375984, -21.10359, 4.04051, -3.7589583, -21.215513, 4.1086473, -3.7589583, -21.082657, 4.045636, -3.7375984, -21.10359, 4.04051, -3.7375984, -21.219786, 4.087715, -3.7589583, -21.215513, 4.1086473, -3.7589583, -21.205261, 3.4823828, -3.7803178, -21.105513, 3.5353546, -3.7803178, -21.210388, 3.5031013, -3.7589583, -21.205261, 3.4823828, -3.7589583, -21.084793, 3.5306554, -3.7803178, -21.105513, 3.5353546, -3.7803178, -21.210388, 3.5031013, -3.7589583, -21.126444, 3.5398402, -3.7589583, -21.215513, 3.5238204, -3.7803178, -21.210388, 3.5031013, -3.7803178, -21.105513, 3.5353546, -3.7589583, -21.126444, 3.5398402, -3.7803178, -21.105513, 3.5353546, -3.7589583, -21.124308, 4.0355973, -3.7589583, -21.126444, 3.5398402, -3.7803178, -21.105513, 3.5353546, -3.7803178, -21.10359, 4.04051, -3.7589583, -21.124308, 4.0355973, -3.7803178, -21.10359, 4.04051, -3.7589583, -21.224058, 4.0667825, -3.7589583, -21.124308, 4.0355973, -3.7803178, -21.10359, 4.04051, -3.7803178, -21.219786, 4.087715, -3.7589583, -21.224058, 4.0667825, -3.7589583, -21.084793, 3.5306554, -3.7803178, -21.10359, 4.04051, -3.7803178, -21.105513, 3.5353546, -3.7589583, -21.084793, 3.5306554, -3.7589583, -21.082657, 4.045636, -3.7803178, -21.10359, 4.04051, -3.7589583, -21.082657, 4.045636, -3.7803178, -21.219786, 4.087715, -3.7803178, -21.10359, 4.04051, -3.7589583, -21.082657, 4.045636, -3.7589583, -21.215513, 4.1086473, -3.7803178, -21.219786, 4.087715, -3.7589583, -21.224058, 4.0667825, -3.7375984, -21.340467, 4.044782, -3.7375984, -21.219786, 4.087715, -3.7589583, -21.224058, 4.0667825, -3.7589583, -21.320816, 4.036238, -3.7375984, -21.340467, 4.044782, -3.7375984, -21.219786, 4.087715, -3.7589583, -21.360119, 4.0533257, -3.7589583, -21.215513, 4.1086473, -3.7375984, -21.219786, 4.087715, -3.7375984, -21.340467, 4.044782, -3.7589583, -21.360119, 4.0533257, -3.7589583, -21.215513, 4.1086473, -3.7803178, -21.340467, 4.044782, -3.7803178, -21.219786, 4.087715, -3.7589583, -21.215513, 4.1086473, -3.7589583, -21.360119, 4.0533257, -3.7803178, -21.340467, 4.044782, -3.7803178, -21.219786, 4.087715, -3.7589583, -21.320816, 4.036238, -3.7589583, -21.224058, 4.0667825, -3.7803178, -21.219786, 4.087715, -3.7803178, -21.340467, 4.044782, -3.7589583, -21.320816, 4.036238, -7.491342, -21.333633, 1.4549251, -7.4672065, -21.35008, 1.0367032, -7.4672065, -21.355635, 1.4619738, -7.491342, -21.333633, 1.4549251, -7.491342, -21.328293, 1.0439655, -7.4672065, -21.35008, 1.0367032, -7.4672065, -21.355635, 1.4619738, -7.491342, -21.371868, 1.0292273, -7.491342, -21.377848, 1.4688089, -7.4672065, -21.355635, 1.4619738, -7.4672065, -21.35008, 1.0367032, -7.491342, -21.371868, 1.0292273, -7.491342, -21.377848, 1.4688089, -7.5154786, -21.35008, 1.0367032, -7.5154786, -21.355635, 1.4619738, -7.491342, -21.377848, 1.4688089, -7.491342, -21.371868, 1.0292273, -7.5154786, -21.35008, 1.0367032, -7.5154786, -21.355635, 1.4619738, -7.491342, -21.328293, 1.0439655, -7.491342, -21.333633, 1.4549251, -7.5154786, -21.355635, 1.4619738, -7.5154786, -21.35008, 1.0367032, -7.491342, -21.328293, 1.0439655, -7.491342, -21.328293, 1.0439655, -7.4672065, -21.208893, 1.0206835, -7.4672065, -21.35008, 1.0367032, -7.491342, -21.328293, 1.0439655, -7.491342, -21.21466, 1.0375576, -7.4672065, -21.208893, 1.0206835, -7.4672065, -21.35008, 1.0367032, -7.491342, -21.20334, 1.0038093, -7.491342, -21.371868, 1.0292273, -7.4672065, -21.35008, 1.0367032, -7.4672065, -21.208893, 1.0206835, -7.491342, -21.20334, 1.0038093, -7.491342, -21.371868, 1.0292273, -7.5154786, -21.208893, 1.0206835, -7.5154786, -21.35008, 1.0367032, -7.491342, -21.371868, 1.0292273, -7.491342, -21.20334, 1.0038093, -7.5154786, -21.208893, 1.0206835, -7.5154786, -21.35008, 1.0367032, -7.491342, -21.21466, 1.0375576, -7.491342, -21.328293, 1.0439655, -7.5154786, -21.35008, 1.0367032, -7.5154786, -21.208893, 1.0206835, -7.491342, -21.21466, 1.0375576, -7.491342, -21.21466, 1.0375576, -7.4672065, -21.090775, 1.0469557, -7.4672065, -21.208893, 1.0206835, -7.491342, -21.21466, 1.0375576, -7.491342, -21.11427, 1.0505869, -7.4672065, -21.090775, 1.0469557, -7.4672065, -21.208893, 1.0206835, -7.491342, -21.067278, 1.0431108, -7.491342, -21.20334, 1.0038093, -7.4672065, -21.208893, 1.0206835, -7.4672065, -21.090775, 1.0469557, -7.491342, -21.067278, 1.0431108, -7.491342, -21.20334, 1.0038093, -7.5154786, -21.090775, 1.0469557, -7.5154786, -21.208893, 1.0206835, -7.491342, -21.20334, 1.0038093, -7.491342, -21.067278, 1.0431108, -7.5154786, -21.090775, 1.0469557, -7.5154786, -21.208893, 1.0206835, -7.491342, -21.11427, 1.0505869, -7.491342, -21.21466, 1.0375576, -7.5154786, -21.208893, 1.0206835, -7.5154786, -21.090775, 1.0469557, -7.491342, -21.11427, 1.0505869, -7.491342, -21.11427, 1.0505869, -7.4672065, -21.088425, 1.4585562, -7.4672065, -21.090775, 1.0469557, -7.491342, -21.11427, 1.0505869, -7.491342, -21.111706, 1.4542842, -7.4672065, -21.088425, 1.4585562, -7.4672065, -21.090775, 1.0469557, -7.491342, -21.06493, 1.4626144, -7.491342, -21.067278, 1.0431108, -7.4672065, -21.090775, 1.0469557, -7.4672065, -21.088425, 1.4585562, -7.491342, -21.06493, 1.4626144, -7.491342, -21.067278, 1.0431108, -7.5154786, -21.088425, 1.4585562, -7.5154786, -21.090775, 1.0469557, -7.491342, -21.067278, 1.0431108, -7.491342, -21.06493, 1.4626144, -7.5154786, -21.088425, 1.4585562, -7.5154786, -21.090775, 1.0469557, -7.491342, -21.111706, 1.4542842, -7.491342, -21.11427, 1.0505869, -7.5154786, -21.090775, 1.0469557, -7.5154786, -21.088425, 1.4585562, -7.491342, -21.111706, 1.4542842, -7.491342, -21.111706, 1.4542842, -7.4672065, -21.219572, 1.4970036, -7.4672065, -21.088425, 1.4585562, -7.491342, -21.111706, 1.4542842, -7.491342, -21.224485, 1.4799159, -7.4672065, -21.219572, 1.4970036, -7.4672065, -21.088425, 1.4585562, -7.491342, -21.214874, 1.5138776, -7.491342, -21.06493, 1.4626144, -7.4672065, -21.088425, 1.4585562, -7.4672065, -21.219572, 1.4970036, -7.491342, -21.214874, 1.5138776, -7.491342, -21.06493, 1.4626144, -7.5154786, -21.219572, 1.4970036, -7.5154786, -21.088425, 1.4585562, -7.491342, -21.06493, 1.4626144, -7.491342, -21.214874, 1.5138776, -7.5154786, -21.219572, 1.4970036, -7.5154786, -21.088425, 1.4585562, -7.491342, -21.224485, 1.4799159, -7.491342, -21.111706, 1.4542842, -7.5154786, -21.088425, 1.4585562, -7.5154786, -21.219572, 1.4970036, -7.491342, -21.224485, 1.4799159, -7.491342, -21.224485, 1.4799159, -7.4672065, -21.355635, 1.4619738, -7.4672065, -21.219572, 1.4970036, -7.491342, -21.224485, 1.4799159, -7.491342, -21.333633, 1.4549251, -7.4672065, -21.355635, 1.4619738, -7.4672065, -21.219572, 1.4970036, -7.491342, -21.377848, 1.4688089, -7.491342, -21.214874, 1.5138776, -7.4672065, -21.219572, 1.4970036, -7.4672065, -21.355635, 1.4619738, -7.491342, -21.377848, 1.4688089, -7.491342, -21.214874, 1.5138776, -7.5154786, -21.355635, 1.4619738, -7.5154786, -21.219572, 1.4970036, -7.491342, -21.214874, 1.5138776, -7.491342, -21.377848, 1.4688089, -7.5154786, -21.355635, 1.4619738, -7.5154786, -21.219572, 1.4970036, -7.491342, -21.333633, 1.4549251, -7.491342, -21.224485, 1.4799159, -7.5154786, -21.219572, 1.4970036, -7.5154786, -21.355635, 1.4619738, -7.491342, -21.333633, 1.4549251, -7.4881387, -21.333633, 2.66858, -7.464002, -21.35008, 2.2503583, -7.464002, -21.355633, 2.6756287, -7.4881387, -21.333633, 2.66858, -7.4881387, -21.328293, 2.2576206, -7.464002, -21.35008, 2.2503583, -7.464002, -21.355633, 2.6756287, -7.4881387, -21.371866, 2.2428825, -7.4881387, -21.377848, 2.682464, -7.464002, -21.355633, 2.6756287, -7.464002, -21.35008, 2.2503583, -7.4881387, -21.371866, 2.2428825, -7.4881387, -21.377848, 2.682464, -7.512275, -21.35008, 2.2503583, -7.512275, -21.355633, 2.6756287, -7.4881387, -21.377848, 2.682464, -7.4881387, -21.371866, 2.2428825, -7.512275, -21.35008, 2.2503583, -7.512275, -21.355633, 2.6756287, -7.4881387, -21.328293, 2.2576206, -7.4881387, -21.333633, 2.66858, -7.512275, -21.355633, 2.6756287, -7.512275, -21.35008, 2.2503583, -7.4881387, -21.328293, 2.2576206, -7.4881387, -21.328293, 2.2576206, -7.464002, -21.208893, 2.2343385, -7.464002, -21.35008, 2.2503583, -7.4881387, -21.328293, 2.2576206, -7.4881387, -21.21466, 2.2512126, -7.464002, -21.208893, 2.2343385, -7.464002, -21.35008, 2.2503583, -7.4881387, -21.203339, 2.2174644, -7.4881387, -21.371866, 2.2428825, -7.464002, -21.35008, 2.2503583, -7.464002, -21.208893, 2.2343385, -7.4881387, -21.203339, 2.2174644, -7.4881387, -21.371866, 2.2428825, -7.512275, -21.208893, 2.2343385, -7.512275, -21.35008, 2.2503583, -7.4881387, -21.371866, 2.2428825, -7.4881387, -21.203339, 2.2174644, -7.512275, -21.208893, 2.2343385, -7.512275, -21.35008, 2.2503583, -7.4881387, -21.21466, 2.2512126, -7.4881387, -21.328293, 2.2576206, -7.512275, -21.35008, 2.2503583, -7.512275, -21.208893, 2.2343385, -7.4881387, -21.21466, 2.2512126, -7.4881387, -21.21466, 2.2512126, -7.464002, -21.090775, 2.2606108, -7.464002, -21.208893, 2.2343385, -7.4881387, -21.21466, 2.2512126, -7.4881387, -21.11427, 2.264242, -7.464002, -21.090775, 2.2606108, -7.464002, -21.208893, 2.2343385, -7.4881387, -21.067278, 2.256766, -7.4881387, -21.203339, 2.2174644, -7.464002, -21.208893, 2.2343385, -7.464002, -21.090775, 2.2606108, -7.4881387, -21.067278, 2.256766, -7.4881387, -21.203339, 2.2174644, -7.512275, -21.090775, 2.2606108, -7.512275, -21.208893, 2.2343385, -7.4881387, -21.203339, 2.2174644, -7.4881387, -21.067278, 2.256766, -7.512275, -21.090775, 2.2606108, -7.512275, -21.208893, 2.2343385, -7.4881387, -21.11427, 2.264242, -7.4881387, -21.21466, 2.2512126, -7.512275, -21.208893, 2.2343385, -7.512275, -21.090775, 2.2606108, -7.4881387, -21.11427, 2.264242, -7.4881387, -21.11427, 2.264242, -7.464002, -21.088425, 2.6722112, -7.464002, -21.090775, 2.2606108, -7.4881387, -21.11427, 2.264242, -7.4881387, -21.111706, 2.6679392, -7.464002, -21.088425, 2.6722112, -7.464002, -21.090775, 2.2606108, -7.4881387, -21.064928, 2.6762695, -7.4881387, -21.067278, 2.256766, -7.464002, -21.090775, 2.2606108, -7.464002, -21.088425, 2.6722112, -7.4881387, -21.064928, 2.6762695, -7.4881387, -21.067278, 2.256766, -7.512275, -21.088425, 2.6722112, -7.512275, -21.090775, 2.2606108, -7.4881387, -21.067278, 2.256766, -7.4881387, -21.064928, 2.6762695, -7.512275, -21.088425, 2.6722112, -7.512275, -21.090775, 2.2606108, -7.4881387, -21.111706, 2.6679392, -7.4881387, -21.11427, 2.264242, -7.512275, -21.090775, 2.2606108, -7.512275, -21.088425, 2.6722112, -7.4881387, -21.111706, 2.6679392, -7.4881387, -21.111706, 2.6679392, -7.464002, -21.219572, 2.7106586, -7.464002, -21.088425, 2.6722112, -7.4881387, -21.111706, 2.6679392, -7.4881387, -21.224485, 2.6935709, -7.464002, -21.219572, 2.7106586, -7.464002, -21.088425, 2.6722112, -7.4881387, -21.214874, 2.7275326, -7.4881387, -21.064928, 2.6762695, -7.464002, -21.088425, 2.6722112, -7.464002, -21.219572, 2.7106586, -7.4881387, -21.214874, 2.7275326, -7.4881387, -21.064928, 2.6762695, -7.512275, -21.219572, 2.7106586, -7.512275, -21.088425, 2.6722112, -7.4881387, -21.064928, 2.6762695, -7.4881387, -21.214874, 2.7275326, -7.512275, -21.219572, 2.7106586, -7.512275, -21.088425, 2.6722112, -7.4881387, -21.224485, 2.6935709, -7.4881387, -21.111706, 2.6679392, -7.512275, -21.088425, 2.6722112, -7.512275, -21.219572, 2.7106586, -7.4881387, -21.224485, 2.6935709, -7.4881387, -21.224485, 2.6935709, -7.464002, -21.355633, 2.6756287, -7.464002, -21.219572, 2.7106586, -7.4881387, -21.224485, 2.6935709, -7.4881387, -21.333633, 2.66858, -7.464002, -21.355633, 2.6756287, -7.464002, -21.219572, 2.7106586, -7.4881387, -21.377848, 2.682464, -7.4881387, -21.214874, 2.7275326, -7.464002, -21.219572, 2.7106586, -7.464002, -21.355633, 2.6756287, -7.4881387, -21.377848, 2.682464, -7.4881387, -21.214874, 2.7275326, -7.512275, -21.355633, 2.6756287, -7.512275, -21.219572, 2.7106586, -7.4881387, -21.214874, 2.7275326, -7.4881387, -21.377848, 2.682464, -7.512275, -21.355633, 2.6756287, -7.512275, -21.219572, 2.7106586, -7.4881387, -21.333633, 2.66858, -7.4881387, -21.224485, 2.6935709, -7.512275, -21.219572, 2.7106586, -7.512275, -21.355633, 2.6756287, -7.4881387, -21.333633, 2.66858, -3.7905703, -21.320818, 1.4850421, -3.7692103, -21.335342, 0.9713427, -3.7692103, -21.34047, 1.4933724, -3.7905703, -21.320818, 1.4850421, -3.7905703, -21.316118, 0.98031366, -3.7692103, -21.335342, 0.9713427, -3.7905703, -21.316118, 0.98031366, -3.7692103, -21.210388, 0.95169187, -3.7692103, -21.335342, 0.9713427, -3.7905703, -21.316118, 0.98031366, -3.7905703, -21.215515, 0.9724107, -3.7692103, -21.210388, 0.95169187, -3.7692103, -21.34047, 1.4933724, -3.7905703, -21.35478, 0.9623716, -3.7905703, -21.360119, 1.5019164, -3.7692103, -21.34047, 1.4933724, -3.7692103, -21.335342, 0.9713427, -3.7905703, -21.35478, 0.9623716, -3.7692103, -21.335342, 0.9713427, -3.7905703, -21.205261, 0.93097293, -3.7905703, -21.35478, 0.9623716, -3.7692103, -21.335342, 0.9713427, -3.7692103, -21.210388, 0.95169187, -3.7905703, -21.205261, 0.93097293, -3.7905703, -21.360119, 1.5019164, -3.8119302, -21.335342, 0.9713427, -3.8119302, -21.34047, 1.4933724, -3.7905703, -21.360119, 1.5019164, -3.7905703, -21.35478, 0.9623716, -3.8119302, -21.335342, 0.9713427, -3.7905703, -21.35478, 0.9623716, -3.8119302, -21.210388, 0.95169187, -3.8119302, -21.335342, 0.9713427, -3.7905703, -21.35478, 0.9623716, -3.7905703, -21.205261, 0.93097293, -3.8119302, -21.210388, 0.95169187, -3.8119302, -21.34047, 1.4933724, -3.7905703, -21.316118, 0.98031366, -3.7905703, -21.320818, 1.4850421, -3.8119302, -21.34047, 1.4933724, -3.8119302, -21.335342, 0.9713427, -3.7905703, -21.316118, 0.98031366, -3.8119302, -21.335342, 0.9713427, -3.7905703, -21.215515, 0.9724107, -3.7905703, -21.316118, 0.98031366, -3.8119302, -21.335342, 0.9713427, -3.8119302, -21.210388, 0.95169187, -3.7905703, -21.215515, 0.9724107, -3.7905703, -21.215515, 0.9724107, -3.7692103, -21.105513, 0.9839448, -3.7692103, -21.210388, 0.95169187, -3.7905703, -21.215515, 0.9724107, -3.7905703, -21.126446, 0.9884304, -3.7692103, -21.105513, 0.9839448, -3.7905703, -21.126446, 0.9884304, -3.7692103, -21.10359, 1.489314, -3.7692103, -21.105513, 0.9839448, -3.7905703, -21.126446, 0.9884304, -3.7905703, -21.12431, 1.4841876, -3.7692103, -21.10359, 1.489314, -3.7905703, -21.12431, 1.4841876, -3.7692103, -21.219786, 1.5363052, -3.7692103, -21.10359, 1.489314, -3.7905703, -21.12431, 1.4841876, -3.7905703, -21.224058, 1.5153728, -3.7692103, -21.219786, 1.5363052, -3.7692103, -21.210388, 0.95169187, -3.7905703, -21.084793, 0.9792458, -3.7905703, -21.205261, 0.93097293, -3.7692103, -21.210388, 0.95169187, -3.7692103, -21.105513, 0.9839448, -3.7905703, -21.084793, 0.9792458, -3.7692103, -21.105513, 0.9839448, -3.7905703, -21.082657, 1.4942267, -3.7905703, -21.084793, 0.9792458, -3.7692103, -21.105513, 0.9839448, -3.7692103, -21.10359, 1.489314, -3.7905703, -21.082657, 1.4942267, -3.7692103, -21.10359, 1.489314, -3.7905703, -21.215515, 1.5574512, -3.7905703, -21.082657, 1.4942267, -3.7692103, -21.10359, 1.489314, -3.7692103, -21.219786, 1.5363052, -3.7905703, -21.215515, 1.5574512, -3.7905703, -21.205261, 0.93097293, -3.8119302, -21.105513, 0.9839448, -3.8119302, -21.210388, 0.95169187, -3.7905703, -21.205261, 0.93097293, -3.7905703, -21.084793, 0.9792458, -3.8119302, -21.105513, 0.9839448, -3.8119302, -21.210388, 0.95169187, -3.7905703, -21.126446, 0.9884304, -3.7905703, -21.215515, 0.9724107, -3.8119302, -21.210388, 0.95169187, -3.8119302, -21.105513, 0.9839448, -3.7905703, -21.126446, 0.9884304, -3.8119302, -21.105513, 0.9839448, -3.7905703, -21.12431, 1.4841876, -3.7905703, -21.126446, 0.9884304, -3.8119302, -21.105513, 0.9839448, -3.8119302, -21.10359, 1.489314, -3.7905703, -21.12431, 1.4841876, -3.8119302, -21.10359, 1.489314, -3.7905703, -21.224058, 1.5153728, -3.7905703, -21.12431, 1.4841876, -3.8119302, -21.10359, 1.489314, -3.8119302, -21.219786, 1.5363052, -3.7905703, -21.224058, 1.5153728, -3.7905703, -21.084793, 0.9792458, -3.8119302, -21.10359, 1.489314, -3.8119302, -21.105513, 0.9839448, -3.7905703, -21.084793, 0.9792458, -3.7905703, -21.082657, 1.4942267, -3.8119302, -21.10359, 1.489314, -3.7905703, -21.082657, 1.4942267, -3.8119302, -21.219786, 1.5363052, -3.8119302, -21.10359, 1.489314, -3.7905703, -21.082657, 1.4942267, -3.7905703, -21.215515, 1.5574512, -3.8119302, -21.219786, 1.5363052, -3.7905703, -21.224058, 1.5153728, -3.7692103, -21.34047, 1.4933724, -3.7692103, -21.219786, 1.5363052, -3.7905703, -21.224058, 1.5153728, -3.7905703, -21.320818, 1.4850421, -3.7692103, -21.34047, 1.4933724, -3.7692103, -21.219786, 1.5363052, -3.7905703, -21.360119, 1.5019164, -3.7905703, -21.215515, 1.5574512, -3.7692103, -21.219786, 1.5363052, -3.7692103, -21.34047, 1.4933724, -3.7905703, -21.360119, 1.5019164, -3.7905703, -21.215515, 1.5574512, -3.8119302, -21.34047, 1.4933724, -3.8119302, -21.219786, 1.5363052, -3.7905703, -21.215515, 1.5574512, -3.7905703, -21.360119, 1.5019164, -3.8119302, -21.34047, 1.4933724, -3.8119302, -21.219786, 1.5363052, -3.7905703, -21.320818, 1.4850421, -3.7905703, -21.224058, 1.5153728, -3.8119302, -21.219786, 1.5363052, -3.8119302, -21.34047, 1.4933724, -3.7905703, -21.320818, 1.4850421, -3.8458915, -21.320818, 2.7324455, -3.824532, -21.335342, 2.218746, -3.824532, -21.340467, 2.7409892, -3.8458915, -21.320818, 2.7324455, -3.8458915, -21.316118, 2.227717, -3.824532, -21.335342, 2.218746, -3.8458915, -21.316118, 2.227717, -3.824532, -21.210388, 2.199095, -3.824532, -21.335342, 2.218746, -3.8458915, -21.316118, 2.227717, -3.8458915, -21.215515, 2.2200274, -3.824532, -21.210388, 2.199095, -3.824532, -21.340467, 2.7409892, -3.8458915, -21.354778, 2.209775, -3.8458915, -21.360119, 2.7495332, -3.824532, -21.340467, 2.7409892, -3.824532, -21.335342, 2.218746, -3.8458915, -21.354778, 2.209775, -3.824532, -21.335342, 2.218746, -3.8458915, -21.205261, 2.1783762, -3.8458915, -21.354778, 2.209775, -3.824532, -21.335342, 2.218746, -3.824532, -21.210388, 2.199095, -3.8458915, -21.205261, 2.1783762, -3.8458915, -21.360119, 2.7495332, -3.8672514, -21.335342, 2.218746, -3.8672514, -21.340467, 2.7409892, -3.8458915, -21.360119, 2.7495332, -3.8458915, -21.354778, 2.209775, -3.8672514, -21.335342, 2.218746, -3.8458915, -21.354778, 2.209775, -3.8672514, -21.210388, 2.199095, -3.8672514, -21.335342, 2.218746, -3.8458915, -21.354778, 2.209775, -3.8458915, -21.205261, 2.1783762, -3.8672514, -21.210388, 2.199095, -3.8672514, -21.340467, 2.7409892, -3.8458915, -21.316118, 2.227717, -3.8458915, -21.320818, 2.7324455, -3.8672514, -21.340467, 2.7409892, -3.8672514, -21.335342, 2.218746, -3.8458915, -21.316118, 2.227717, -3.8672514, -21.335342, 2.218746, -3.8458915, -21.215515, 2.2200274, -3.8458915, -21.316118, 2.227717, -3.8672514, -21.335342, 2.218746, -3.8672514, -21.210388, 2.199095, -3.8458915, -21.215515, 2.2200274, -3.8458915, -21.215515, 2.2200274, -3.824532, -21.105513, 2.231348, -3.824532, -21.210388, 2.199095, -3.8458915, -21.215515, 2.2200274, -3.8458915, -21.126444, 2.2360473, -3.824532, -21.105513, 2.231348, -3.8458915, -21.126444, 2.2360473, -3.824532, -21.10359, 2.7367172, -3.824532, -21.105513, 2.231348, -3.8458915, -21.126444, 2.2360473, -3.8458915, -21.124308, 2.731591, -3.824532, -21.10359, 2.7367172, -3.8458915, -21.124308, 2.731591, -3.824532, -21.219786, 2.7839222, -3.824532, -21.10359, 2.7367172, -3.8458915, -21.124308, 2.731591, -3.8458915, -21.224058, 2.7629898, -3.824532, -21.219786, 2.7839222, -3.824532, -21.210388, 2.199095, -3.8458915, -21.084793, 2.2268624, -3.8458915, -21.205261, 2.1783762, -3.824532, -21.210388, 2.199095, -3.824532, -21.105513, 2.231348, -3.8458915, -21.084793, 2.2268624, -3.824532, -21.105513, 2.231348, -3.8458915, -21.082657, 2.7418437, -3.8458915, -21.084793, 2.2268624, -3.824532, -21.105513, 2.231348, -3.824532, -21.10359, 2.7367172, -3.8458915, -21.082657, 2.7418437, -3.824532, -21.10359, 2.7367172, -3.8458915, -21.215513, 2.8048546, -3.8458915, -21.082657, 2.7418437, -3.824532, -21.10359, 2.7367172, -3.824532, -21.219786, 2.7839222, -3.8458915, -21.215513, 2.8048546, -3.8458915, -21.205261, 2.1783762, -3.8672514, -21.105513, 2.231348, -3.8672514, -21.210388, 2.199095, -3.8458915, -21.205261, 2.1783762, -3.8458915, -21.084793, 2.2268624, -3.8672514, -21.105513, 2.231348, -3.8672514, -21.210388, 2.199095, -3.8458915, -21.126444, 2.2360473, -3.8458915, -21.215515, 2.2200274, -3.8672514, -21.210388, 2.199095, -3.8672514, -21.105513, 2.231348, -3.8458915, -21.126444, 2.2360473, -3.8672514, -21.105513, 2.231348, -3.8458915, -21.124308, 2.731591, -3.8458915, -21.126444, 2.2360473, -3.8672514, -21.105513, 2.231348, -3.8672514, -21.10359, 2.7367172, -3.8458915, -21.124308, 2.731591, -3.8672514, -21.10359, 2.7367172, -3.8458915, -21.224058, 2.7629898, -3.8458915, -21.124308, 2.731591, -3.8672514, -21.10359, 2.7367172, -3.8672514, -21.219786, 2.7839222, -3.8458915, -21.224058, 2.7629898, -3.8458915, -21.084793, 2.2268624, -3.8672514, -21.10359, 2.7367172, -3.8672514, -21.105513, 2.231348, -3.8458915, -21.084793, 2.2268624, -3.8458915, -21.082657, 2.7418437, -3.8672514, -21.10359, 2.7367172, -3.8458915, -21.082657, 2.7418437, -3.8672514, -21.219786, 2.7839222, -3.8672514, -21.10359, 2.7367172, -3.8458915, -21.082657, 2.7418437, -3.8458915, -21.215513, 2.8048546, -3.8672514, -21.219786, 2.7839222, -3.8458915, -21.224058, 2.7629898, -3.824532, -21.340467, 2.7409892, -3.824532, -21.219786, 2.7839222, -3.8458915, -21.224058, 2.7629898, -3.8458915, -21.320818, 2.7324455, -3.824532, -21.340467, 2.7409892, -3.824532, -21.219786, 2.7839222, -3.8458915, -21.360119, 2.7495332, -3.8458915, -21.215513, 2.8048546, -3.824532, -21.219786, 2.7839222, -3.824532, -21.340467, 2.7409892, -3.8458915, -21.360119, 2.7495332, -3.8458915, -21.215513, 2.8048546, -3.8672514, -21.340467, 2.7409892, -3.8672514, -21.219786, 2.7839222, -3.8458915, -21.215513, 2.8048546, -3.8458915, -21.360119, 2.7495332, -3.8672514, -21.340467, 2.7409892, -3.8672514, -21.219786, 2.7839222, -3.8458915, -21.320818, 2.7324455, -3.8458915, -21.224058, 2.7629898, -3.8672514, -21.219786, 2.7839222, -3.8672514, -21.340467, 2.7409892, -3.8458915, -21.320818, 2.7324455, -3.774764, -21.320818, 2.140356, -3.7534046, -21.335342, 1.6268702, -3.7534046, -21.340467, 2.1489, -3.774764, -21.320818, 2.140356, -3.774764, -21.316118, 1.6358411, -3.7534046, -21.335342, 1.6268702, -3.774764, -21.316118, 1.6358411, -3.7534046, -21.210388, 1.6072192, -3.7534046, -21.335342, 1.6268702, -3.774764, -21.316118, 1.6358411, -3.774764, -21.215515, 1.6279382, -3.7534046, -21.210388, 1.6072192, -3.7534046, -21.340467, 2.1489, -3.774764, -21.35478, 1.6176856, -3.774764, -21.360119, 2.1574438, -3.7534046, -21.340467, 2.1489, -3.7534046, -21.335342, 1.6268702, -3.774764, -21.35478, 1.6176856, -3.7534046, -21.335342, 1.6268702, -3.774764, -21.205261, 1.5865004, -3.774764, -21.35478, 1.6176856, -3.7534046, -21.335342, 1.6268702, -3.7534046, -21.210388, 1.6072192, -3.774764, -21.205261, 1.5865004, -3.774764, -21.360119, 2.1574438, -3.796124, -21.335342, 1.6268702, -3.796124, -21.340467, 2.1489, -3.774764, -21.360119, 2.1574438, -3.774764, -21.35478, 1.6176856, -3.796124, -21.335342, 1.6268702, -3.774764, -21.35478, 1.6176856, -3.796124, -21.210388, 1.6072192, -3.796124, -21.335342, 1.6268702, -3.774764, -21.35478, 1.6176856, -3.774764, -21.205261, 1.5865004, -3.796124, -21.210388, 1.6072192, -3.796124, -21.340467, 2.1489, -3.774764, -21.316118, 1.6358411, -3.774764, -21.320818, 2.140356, -3.796124, -21.340467, 2.1489, -3.796124, -21.335342, 1.6268702, -3.774764, -21.316118, 1.6358411, -3.796124, -21.335342, 1.6268702, -3.774764, -21.215515, 1.6279382, -3.774764, -21.316118, 1.6358411, -3.796124, -21.335342, 1.6268702, -3.796124, -21.210388, 1.6072192, -3.774764, -21.215515, 1.6279382, -3.774764, -21.215515, 1.6279382, -3.7534046, -21.105513, 1.6392586, -3.7534046, -21.210388, 1.6072192, -3.774764, -21.215515, 1.6279382, -3.774764, -21.126446, 1.6439579, -3.7534046, -21.105513, 1.6392586, -3.774764, -21.126446, 1.6439579, -3.7534046, -21.10359, 2.144628, -3.7534046, -21.105513, 1.6392586, -3.774764, -21.126446, 1.6439579, -3.774764, -21.12431, 2.1395016, -3.7534046, -21.10359, 2.144628, -3.774764, -21.12431, 2.1395016, -3.7534046, -21.219786, 2.1918328, -3.7534046, -21.10359, 2.144628, -3.774764, -21.12431, 2.1395016, -3.774764, -21.224058, 2.1709003, -3.7534046, -21.219786, 2.1918328, -3.7534046, -21.210388, 1.6072192, -3.774764, -21.084793, 1.6347731, -3.774764, -21.205261, 1.5865004, -3.7534046, -21.210388, 1.6072192, -3.7534046, -21.105513, 1.6392586, -3.774764, -21.084793, 1.6347731, -3.7534046, -21.105513, 1.6392586, -3.774764, -21.082657, 2.1497543, -3.774764, -21.084793, 1.6347731, -3.7534046, -21.105513, 1.6392586, -3.7534046, -21.10359, 2.144628, -3.774764, -21.082657, 2.1497543, -3.7534046, -21.10359, 2.144628, -3.774764, -21.215515, 2.2127652, -3.774764, -21.082657, 2.1497543, -3.7534046, -21.10359, 2.144628, -3.7534046, -21.219786, 2.1918328, -3.774764, -21.215515, 2.2127652, -3.774764, -21.205261, 1.5865004, -3.796124, -21.105513, 1.6392586, -3.796124, -21.210388, 1.6072192, -3.774764, -21.205261, 1.5865004, -3.774764, -21.084793, 1.6347731, -3.796124, -21.105513, 1.6392586, -3.796124, -21.210388, 1.6072192, -3.774764, -21.126446, 1.6439579, -3.774764, -21.215515, 1.6279382, -3.796124, -21.210388, 1.6072192, -3.796124, -21.105513, 1.6392586, -3.774764, -21.126446, 1.6439579, -3.796124, -21.105513, 1.6392586, -3.774764, -21.12431, 2.1395016, -3.774764, -21.126446, 1.6439579, -3.796124, -21.105513, 1.6392586, -3.796124, -21.10359, 2.144628, -3.774764, -21.12431, 2.1395016, -3.796124, -21.10359, 2.144628, -3.774764, -21.224058, 2.1709003, -3.774764, -21.12431, 2.1395016, -3.796124, -21.10359, 2.144628, -3.796124, -21.219786, 2.1918328, -3.774764, -21.224058, 2.1709003, -3.774764, -21.084793, 1.6347731, -3.796124, -21.10359, 2.144628, -3.796124, -21.105513, 1.6392586, -3.774764, -21.084793, 1.6347731, -3.774764, -21.082657, 2.1497543, -3.796124, -21.10359, 2.144628, -3.774764, -21.082657, 2.1497543, -3.796124, -21.219786, 2.1918328, -3.796124, -21.10359, 2.144628, -3.774764, -21.082657, 2.1497543, -3.774764, -21.215515, 2.2127652, -3.796124, -21.219786, 2.1918328, -3.774764, -21.224058, 2.1709003, -3.7534046, -21.340467, 2.1489, -3.7534046, -21.219786, 2.1918328, -3.774764, -21.224058, 2.1709003, -3.774764, -21.320818, 2.140356, -3.7534046, -21.340467, 2.1489, -3.7534046, -21.219786, 2.1918328, -3.774764, -21.360119, 2.1574438, -3.774764, -21.215515, 2.2127652, -3.7534046, -21.219786, 2.1918328, -3.7534046, -21.340467, 2.1489, -3.774764, -21.360119, 2.1574438, -3.774764, -21.215515, 2.2127652, -3.796124, -21.340467, 2.1489, -3.796124, -21.219786, 2.1918328, -3.774764, -21.215515, 2.2127652, -3.774764, -21.360119, 2.1574438, -3.796124, -21.340467, 2.1489, -3.796124, -21.219786, 2.1918328, -3.774764, -21.320818, 2.140356, -3.774764, -21.224058, 2.1709003, -3.796124, -21.219786, 2.1918328, -3.796124, -21.340467, 2.1489, -3.774764, -21.320818, 2.140356, -7.4558854, -21.333633, -0.46060777, -7.431749, -21.35008, -0.89848065, -7.431749, -21.355635, -0.45334554, -7.4558854, -21.333633, -0.46060777, -7.4558854, -21.328295, -0.89057755, -7.431749, -21.35008, -0.89848065, -7.431749, -21.355635, -0.45334554, -7.4558854, -21.371868, -0.9061701, -7.4558854, -21.37785, -0.4460833, -7.431749, -21.355635, -0.45334554, -7.431749, -21.35008, -0.89848065, -7.4558854, -21.371868, -0.9061701, -7.4558854, -21.37785, -0.4460833, -7.480022, -21.35008, -0.89848065, -7.480022, -21.355635, -0.45334554, -7.4558854, -21.37785, -0.4460833, -7.4558854, -21.371868, -0.9061701, -7.480022, -21.35008, -0.89848065, -7.480022, -21.355635, -0.45334554, -7.4558854, -21.328295, -0.89057755, -7.4558854, -21.333633, -0.46060777, -7.480022, -21.355635, -0.45334554, -7.480022, -21.35008, -0.89848065, -7.4558854, -21.328295, -0.89057755, -7.4558854, -21.328295, -0.89057755, -7.431749, -21.208895, -0.9149277, -7.431749, -21.35008, -0.89848065, -7.4558854, -21.328295, -0.89057755, -7.4558854, -21.21466, -0.8974128, -7.431749, -21.208895, -0.9149277, -7.431749, -21.35008, -0.89848065, -7.4558854, -21.20334, -0.9326565, -7.4558854, -21.371868, -0.9061701, -7.431749, -21.35008, -0.89848065, -7.431749, -21.208895, -0.9149277, -7.4558854, -21.20334, -0.9326565, -7.4558854, -21.371868, -0.9061701, -7.480022, -21.208895, -0.9149277, -7.480022, -21.35008, -0.89848065, -7.4558854, -21.371868, -0.9061701, -7.4558854, -21.20334, -0.9326565, -7.480022, -21.208895, -0.9149277, -7.480022, -21.35008, -0.89848065, -7.4558854, -21.21466, -0.8974128, -7.4558854, -21.328295, -0.89057755, -7.480022, -21.35008, -0.89848065, -7.480022, -21.208895, -0.9149277, -7.4558854, -21.21466, -0.8974128, -7.4558854, -21.21466, -0.8974128, -7.431749, -21.090775, -0.88758755, -7.431749, -21.208895, -0.9149277, -7.4558854, -21.21466, -0.8974128, -7.4558854, -21.114271, -0.8837426, -7.431749, -21.090775, -0.88758755, -7.431749, -21.208895, -0.9149277, -7.4558854, -21.06728, -0.89164567, -7.4558854, -21.20334, -0.9326565, -7.431749, -21.208895, -0.9149277, -7.431749, -21.090775, -0.88758755, -7.4558854, -21.06728, -0.89164567, -7.4558854, -21.20334, -0.9326565, -7.480022, -21.090775, -0.88758755, -7.480022, -21.208895, -0.9149277, -7.4558854, -21.20334, -0.9326565, -7.4558854, -21.06728, -0.89164567, -7.480022, -21.090775, -0.88758755, -7.480022, -21.208895, -0.9149277, -7.4558854, -21.114271, -0.8837426, -7.4558854, -21.21466, -0.8974128, -7.480022, -21.208895, -0.9149277, -7.480022, -21.090775, -0.88758755, -7.4558854, -21.114271, -0.8837426, -7.4558854, -21.114271, -0.8837426, -7.431749, -21.088425, -0.45697713, -7.431749, -21.090775, -0.88758755, -7.4558854, -21.114271, -0.8837426, -7.4558854, -21.111708, -0.46124887, -7.431749, -21.088425, -0.45697713, -7.4558854, -21.111708, -0.46124887, -7.431749, -21.219574, -0.416821, -7.431749, -21.088425, -0.45697713, -7.4558854, -21.111708, -0.46124887, -7.4558854, -21.224487, -0.43454933, -7.431749, -21.219574, -0.416821, -7.431749, -21.090775, -0.88758755, -7.4558854, -21.06493, -0.45249128, -7.4558854, -21.06728, -0.89164567, -7.431749, -21.090775, -0.88758755, -7.431749, -21.088425, -0.45697713, -7.4558854, -21.06493, -0.45249128, -7.431749, -21.088425, -0.45697713, -7.4558854, -21.214874, -0.39887857, -7.4558854, -21.06493, -0.45249128, -7.431749, -21.088425, -0.45697713, -7.431749, -21.219574, -0.416821, -7.4558854, -21.214874, -0.39887857, -7.4558854, -21.06728, -0.89164567, -7.480022, -21.088425, -0.45697713, -7.480022, -21.090775, -0.88758755, -7.4558854, -21.06728, -0.89164567, -7.4558854, -21.06493, -0.45249128, -7.480022, -21.088425, -0.45697713, -7.4558854, -21.06493, -0.45249128, -7.480022, -21.219574, -0.416821, -7.480022, -21.088425, -0.45697713, -7.4558854, -21.06493, -0.45249128, -7.4558854, -21.214874, -0.39887857, -7.480022, -21.219574, -0.416821, -7.480022, -21.090775, -0.88758755, -7.4558854, -21.111708, -0.46124887, -7.4558854, -21.114271, -0.8837426, -7.480022, -21.090775, -0.88758755, -7.480022, -21.088425, -0.45697713, -7.4558854, -21.111708, -0.46124887, -7.480022, -21.088425, -0.45697713, -7.4558854, -21.224487, -0.43454933, -7.4558854, -21.111708, -0.46124887, -7.480022, -21.088425, -0.45697713, -7.480022, -21.219574, -0.416821, -7.4558854, -21.224487, -0.43454933, -7.4558854, -21.224487, -0.43454933, -7.431749, -21.355635, -0.45334554, -7.431749, -21.219574, -0.416821, -7.4558854, -21.224487, -0.43454933, -7.4558854, -21.333633, -0.46060777, -7.431749, -21.355635, -0.45334554, -7.431749, -21.219574, -0.416821, -7.4558854, -21.37785, -0.4460833, -7.4558854, -21.214874, -0.39887857, -7.431749, -21.219574, -0.416821, -7.431749, -21.355635, -0.45334554, -7.4558854, -21.37785, -0.4460833, -7.4558854, -21.214874, -0.39887857, -7.480022, -21.355635, -0.45334554, -7.480022, -21.219574, -0.416821, -7.4558854, -21.214874, -0.39887857, -7.4558854, -21.37785, -0.4460833, -7.480022, -21.355635, -0.45334554, -7.480022, -21.219574, -0.416821, -7.4558854, -21.333633, -0.46060777, -7.4558854, -21.224487, -0.43454933, -7.480022, -21.219574, -0.416821, -7.480022, -21.355635, -0.45334554, -7.4558854, -21.333633, -0.46060777, -3.8775039, -21.320818, 0.85066056, -3.8561444, -21.335342, 0.33696115, -3.8561444, -21.34047, 0.85920453, -3.8775039, -21.320818, 0.85066056, -3.8775039, -21.316118, 0.34593213, -3.8561444, -21.335342, 0.33696115, -3.8775039, -21.316118, 0.34593213, -3.8561444, -21.210388, 0.31752384, -3.8561444, -21.335342, 0.33696115, -3.8775039, -21.316118, 0.34593213, -3.8775039, -21.215515, 0.33824265, -3.8561444, -21.210388, 0.31752384, -3.8561444, -21.34047, 0.85920453, -3.8775039, -21.35478, 0.32799017, -3.8775039, -21.360119, 0.8677484, -3.8561444, -21.34047, 0.85920453, -3.8561444, -21.335342, 0.33696115, -3.8775039, -21.35478, 0.32799017, -3.8561444, -21.335342, 0.33696115, -3.8775039, -21.205263, 0.29680502, -3.8775039, -21.35478, 0.32799017, -3.8561444, -21.335342, 0.33696115, -3.8561444, -21.210388, 0.31752384, -3.8775039, -21.205263, 0.29680502, -3.8775039, -21.360119, 0.8677484, -3.8988638, -21.335342, 0.33696115, -3.8988638, -21.34047, 0.85920453, -3.8775039, -21.360119, 0.8677484, -3.8775039, -21.35478, 0.32799017, -3.8988638, -21.335342, 0.33696115, -3.8775039, -21.35478, 0.32799017, -3.8988638, -21.210388, 0.31752384, -3.8988638, -21.335342, 0.33696115, -3.8775039, -21.35478, 0.32799017, -3.8775039, -21.205263, 0.29680502, -3.8988638, -21.210388, 0.31752384, -3.8988638, -21.34047, 0.85920453, -3.8775039, -21.316118, 0.34593213, -3.8775039, -21.320818, 0.85066056, -3.8988638, -21.34047, 0.85920453, -3.8988638, -21.335342, 0.33696115, -3.8775039, -21.316118, 0.34593213, -3.8988638, -21.335342, 0.33696115, -3.8775039, -21.215515, 0.33824265, -3.8775039, -21.316118, 0.34593213, -3.8988638, -21.335342, 0.33696115, -3.8988638, -21.210388, 0.31752384, -3.8775039, -21.215515, 0.33824265, -3.8775039, -21.215515, 0.33824265, -3.8561444, -21.105513, 0.34956324, -3.8561444, -21.210388, 0.31752384, -3.8775039, -21.215515, 0.33824265, -3.8775039, -21.126446, 0.35404897, -3.8561444, -21.105513, 0.34956324, -3.8775039, -21.126446, 0.35404897, -3.8561444, -21.10359, 0.85493267, -3.8561444, -21.105513, 0.34956324, -3.8775039, -21.126446, 0.35404897, -3.8775039, -21.12431, 0.8498062, -3.8561444, -21.10359, 0.85493267, -3.8775039, -21.12431, 0.8498062, -3.8561444, -21.219788, 0.9021374, -3.8561444, -21.10359, 0.85493267, -3.8775039, -21.12431, 0.8498062, -3.8775039, -21.224058, 0.88120484, -3.8561444, -21.219788, 0.9021374, -3.8561444, -21.210388, 0.31752384, -3.8775039, -21.084793, 0.34507775, -3.8775039, -21.205263, 0.29680502, -3.8561444, -21.210388, 0.31752384, -3.8561444, -21.105513, 0.34956324, -3.8775039, -21.084793, 0.34507775, -3.8561444, -21.105513, 0.34956324, -3.8775039, -21.082657, 0.8600589, -3.8775039, -21.084793, 0.34507775, -3.8561444, -21.105513, 0.34956324, -3.8561444, -21.10359, 0.85493267, -3.8775039, -21.082657, 0.8600589, -3.8561444, -21.10359, 0.85493267, -3.8775039, -21.215515, 0.92306983, -3.8775039, -21.082657, 0.8600589, -3.8561444, -21.10359, 0.85493267, -3.8561444, -21.219788, 0.9021374, -3.8775039, -21.215515, 0.92306983, -3.8775039, -21.205263, 0.29680502, -3.8988638, -21.105513, 0.34956324, -3.8988638, -21.210388, 0.31752384, -3.8775039, -21.205263, 0.29680502, -3.8775039, -21.084793, 0.34507775, -3.8988638, -21.105513, 0.34956324, -3.8988638, -21.210388, 0.31752384, -3.8775039, -21.126446, 0.35404897, -3.8775039, -21.215515, 0.33824265, -3.8988638, -21.210388, 0.31752384, -3.8988638, -21.105513, 0.34956324, -3.8775039, -21.126446, 0.35404897, -3.8988638, -21.105513, 0.34956324, -3.8775039, -21.12431, 0.8498062, -3.8775039, -21.126446, 0.35404897, -3.8988638, -21.105513, 0.34956324, -3.8988638, -21.10359, 0.85493267, -3.8775039, -21.12431, 0.8498062, -3.8988638, -21.10359, 0.85493267, -3.8775039, -21.224058, 0.88120484, -3.8775039, -21.12431, 0.8498062, -3.8988638, -21.10359, 0.85493267, -3.8988638, -21.219788, 0.9021374, -3.8775039, -21.224058, 0.88120484, -3.8775039, -21.084793, 0.34507775, -3.8988638, -21.10359, 0.85493267, -3.8988638, -21.105513, 0.34956324, -3.8775039, -21.084793, 0.34507775, -3.8775039, -21.082657, 0.8600589, -3.8988638, -21.10359, 0.85493267, -3.8775039, -21.082657, 0.8600589, -3.8988638, -21.219788, 0.9021374, -3.8988638, -21.10359, 0.85493267, -3.8775039, -21.082657, 0.8600589, -3.8775039, -21.215515, 0.92306983, -3.8988638, -21.219788, 0.9021374, -3.8775039, -21.224058, 0.88120484, -3.8561444, -21.34047, 0.85920453, -3.8561444, -21.219788, 0.9021374, -3.8775039, -21.224058, 0.88120484, -3.8775039, -21.320818, 0.85066056, -3.8561444, -21.34047, 0.85920453, -3.8561444, -21.219788, 0.9021374, -3.8775039, -21.360119, 0.8677484, -3.8775039, -21.215515, 0.92306983, -3.8561444, -21.219788, 0.9021374, -3.8561444, -21.34047, 0.85920453, -3.8775039, -21.360119, 0.8677484, -3.8775039, -21.215515, 0.92306983, -3.8988638, -21.34047, 0.85920453, -3.8988638, -21.219788, 0.9021374, -3.8775039, -21.215515, 0.92306983, -3.8775039, -21.360119, 0.8677484, -3.8988638, -21.34047, 0.85920453, -3.8988638, -21.219788, 0.9021374, -3.8775039, -21.320818, 0.85066056, -3.8775039, -21.224058, 0.88120484, -3.8988638, -21.219788, 0.9021374, -3.8988638, -21.34047, 0.85920453, -3.8775039, -21.320818, 0.85066056, -3.7194428, -21.320816, 3.3595648, -3.698083, -21.335342, 2.8460789, -3.698083, -21.340467, 3.3681085, -3.7194428, -21.320816, 3.3595648, -3.7194428, -21.316118, 2.8550498, -3.698083, -21.335342, 2.8460789, -3.7194428, -21.316118, 2.8550498, -3.698083, -21.210388, 2.826428, -3.698083, -21.335342, 2.8460789, -3.7194428, -21.316118, 2.8550498, -3.7194428, -21.215513, 2.8471467, -3.698083, -21.210388, 2.826428, -3.698083, -21.340467, 3.3681085, -3.7194428, -21.354778, 2.836894, -3.7194428, -21.360119, 3.3768659, -3.698083, -21.340467, 3.3681085, -3.698083, -21.335342, 2.8460789, -3.7194428, -21.354778, 2.836894, -3.698083, -21.335342, 2.8460789, -3.7194428, -21.205261, 2.805709, -3.7194428, -21.354778, 2.836894, -3.698083, -21.335342, 2.8460789, -3.698083, -21.210388, 2.826428, -3.7194428, -21.205261, 2.805709, -3.7194428, -21.360119, 3.3768659, -3.7408023, -21.335342, 2.8460789, -3.7408023, -21.340467, 3.3681085, -3.7194428, -21.360119, 3.3768659, -3.7194428, -21.354778, 2.836894, -3.7408023, -21.335342, 2.8460789, -3.7194428, -21.354778, 2.836894, -3.7408023, -21.210388, 2.826428, -3.7408023, -21.335342, 2.8460789, -3.7194428, -21.354778, 2.836894, -3.7194428, -21.205261, 2.805709, -3.7408023, -21.210388, 2.826428, -3.7408023, -21.340467, 3.3681085, -3.7194428, -21.316118, 2.8550498, -3.7194428, -21.320816, 3.3595648, -3.7408023, -21.340467, 3.3681085, -3.7408023, -21.335342, 2.8460789, -3.7194428, -21.316118, 2.8550498, -3.7408023, -21.335342, 2.8460789, -3.7194428, -21.215513, 2.8471467, -3.7194428, -21.316118, 2.8550498, -3.7408023, -21.335342, 2.8460789, -3.7408023, -21.210388, 2.826428, -3.7194428, -21.215513, 2.8471467, -3.7194428, -21.215513, 2.8471467, -3.698083, -21.105513, 2.8586807, -3.698083, -21.210388, 2.826428, -3.7194428, -21.215513, 2.8471467, -3.7194428, -21.126444, 2.8631663, -3.698083, -21.105513, 2.8586807, -3.7194428, -21.126444, 2.8631663, -3.698083, -21.10359, 3.3640501, -3.698083, -21.105513, 2.8586807, -3.7194428, -21.126444, 2.8631663, -3.7194428, -21.124308, 3.358924, -3.698083, -21.10359, 3.3640501, -3.7194428, -21.124308, 3.358924, -3.698083, -21.219786, 3.411255, -3.698083, -21.10359, 3.3640501, -3.7194428, -21.124308, 3.358924, -3.7194428, -21.224058, 3.3903227, -3.698083, -21.219786, 3.411255, -3.698083, -21.210388, 2.826428, -3.7194428, -21.084793, 2.8539817, -3.7194428, -21.205261, 2.805709, -3.698083, -21.210388, 2.826428, -3.698083, -21.105513, 2.8586807, -3.7194428, -21.084793, 2.8539817, -3.698083, -21.105513, 2.8586807, -3.7194428, -21.082657, 3.3691764, -3.7194428, -21.084793, 2.8539817, -3.698083, -21.105513, 2.8586807, -3.698083, -21.10359, 3.3640501, -3.7194428, -21.082657, 3.3691764, -3.698083, -21.10359, 3.3640501, -3.7194428, -21.215513, 3.431974, -3.7194428, -21.082657, 3.3691764, -3.698083, -21.10359, 3.3640501, -3.698083, -21.219786, 3.411255, -3.7194428, -21.215513, 3.431974, -3.7194428, -21.205261, 2.805709, -3.7408023, -21.105513, 2.8586807, -3.7408023, -21.210388, 2.826428, -3.7194428, -21.205261, 2.805709, -3.7194428, -21.084793, 2.8539817, -3.7408023, -21.105513, 2.8586807, -3.7408023, -21.210388, 2.826428, -3.7194428, -21.126444, 2.8631663, -3.7194428, -21.215513, 2.8471467, -3.7408023, -21.210388, 2.826428, -3.7408023, -21.105513, 2.8586807, -3.7194428, -21.126444, 2.8631663, -3.7408023, -21.105513, 2.8586807, -3.7194428, -21.124308, 3.358924, -3.7194428, -21.126444, 2.8631663, -3.7408023, -21.105513, 2.8586807, -3.7408023, -21.10359, 3.3640501, -3.7194428, -21.124308, 3.358924, -3.7408023, -21.10359, 3.3640501, -3.7194428, -21.224058, 3.3903227, -3.7194428, -21.124308, 3.358924, -3.7408023, -21.10359, 3.3640501, -3.7408023, -21.219786, 3.411255, -3.7194428, -21.224058, 3.3903227, -3.7194428, -21.084793, 2.8539817, -3.7408023, -21.10359, 3.3640501, -3.7408023, -21.105513, 2.8586807, -3.7194428, -21.084793, 2.8539817, -3.7194428, -21.082657, 3.3691764, -3.7408023, -21.10359, 3.3640501, -3.7194428, -21.082657, 3.3691764, -3.7408023, -21.219786, 3.411255, -3.7408023, -21.10359, 3.3640501, -3.7194428, -21.082657, 3.3691764, -3.7194428, -21.215513, 3.431974, -3.7408023, -21.219786, 3.411255, -3.7194428, -21.224058, 3.3903227, -3.698083, -21.340467, 3.3681085, -3.698083, -21.219786, 3.411255, -3.7194428, -21.224058, 3.3903227, -3.7194428, -21.320816, 3.3595648, -3.698083, -21.340467, 3.3681085, -3.698083, -21.219786, 3.411255, -3.7194428, -21.360119, 3.3768659, -3.7194428, -21.215513, 3.431974, -3.698083, -21.219786, 3.411255, -3.698083, -21.340467, 3.3681085, -3.7194428, -21.360119, 3.3768659, -3.7194428, -21.215513, 3.431974, -3.7408023, -21.340467, 3.3681085, -3.7408023, -21.219786, 3.411255, -3.7194428, -21.215513, 3.431974, -3.7194428, -21.360119, 3.3768659, -3.7408023, -21.340467, 3.3681085, -3.7408023, -21.219786, 3.411255, -3.7194428, -21.320816, 3.3595648, -3.7194428, -21.224058, 3.3903227, -3.7408023, -21.219786, 3.411255, -3.7408023, -21.340467, 3.3681085, -3.7194428, -21.320816, 3.3595648, -7.4172244, -21.333633, 4.035811, -7.393088, -21.35008, 3.6370263, -7.393088, -21.355633, 4.0424323, -7.4172244, -21.333633, 4.035811, -7.4172244, -21.328293, 3.6438615, -7.393088, -21.35008, 3.6370263, -7.393088, -21.355633, 4.0424323, -7.4172244, -21.371866, 3.6299777, -7.4172244, -21.377848, 4.049054, -7.393088, -21.355633, 4.0424323, -7.393088, -21.35008, 3.6370263, -7.4172244, -21.371866, 3.6299777, -7.4172244, -21.377848, 4.049054, -7.441361, -21.35008, 3.6370263, -7.441361, -21.355633, 4.0424323, -7.4172244, -21.377848, 4.049054, -7.4172244, -21.371866, 3.6299777, -7.441361, -21.35008, 3.6370263, -7.441361, -21.355633, 4.0424323, -7.4172244, -21.328293, 3.6438615, -7.4172244, -21.333633, 4.035811, -7.441361, -21.355633, 4.0424323, -7.441361, -21.35008, 3.6370263, -7.4172244, -21.328293, 3.6438615, -7.4172244, -21.328293, 3.6438615, -7.393088, -21.208893, 3.621861, -7.393088, -21.35008, 3.6370263, -7.4172244, -21.328293, 3.6438615, -7.4172244, -21.214659, 3.6378808, -7.393088, -21.208893, 3.621861, -7.393088, -21.35008, 3.6370263, -7.4172244, -21.203339, 3.6056278, -7.4172244, -21.371866, 3.6299777, -7.393088, -21.35008, 3.6370263, -7.393088, -21.208893, 3.621861, -7.4172244, -21.203339, 3.6056278, -7.4172244, -21.371866, 3.6299777, -7.441361, -21.208893, 3.621861, -7.441361, -21.35008, 3.6370263, -7.4172244, -21.371866, 3.6299777, -7.4172244, -21.203339, 3.6056278, -7.441361, -21.208893, 3.621861, -7.441361, -21.35008, 3.6370263, -7.4172244, -21.214659, 3.6378808, -7.4172244, -21.328293, 3.6438615, -7.441361, -21.35008, 3.6370263, -7.441361, -21.208893, 3.621861, -7.4172244, -21.214659, 3.6378808, -7.4172244, -21.214659, 3.6378808, -7.393088, -21.090773, 3.6466384, -7.393088, -21.208893, 3.621861, -7.4172244, -21.214659, 3.6378808, -7.4172244, -21.11427, 3.6502695, -7.393088, -21.090773, 3.6466384, -7.393088, -21.208893, 3.621861, -7.4172244, -21.067278, 3.6432207, -7.4172244, -21.203339, 3.6056278, -7.393088, -21.208893, 3.621861, -7.393088, -21.090773, 3.6466384, -7.4172244, -21.067278, 3.6432207, -7.4172244, -21.203339, 3.6056278, -7.441361, -21.090773, 3.6466384, -7.441361, -21.208893, 3.621861, -7.4172244, -21.203339, 3.6056278, -7.4172244, -21.067278, 3.6432207, -7.441361, -21.090773, 3.6466384, -7.441361, -21.208893, 3.621861, -7.4172244, -21.11427, 3.6502695, -7.4172244, -21.214659, 3.6378808, -7.441361, -21.208893, 3.621861, -7.441361, -21.090773, 3.6466384, -7.4172244, -21.11427, 3.6502695, -7.4172244, -21.11427, 3.6502695, -7.393088, -21.088425, 4.0392284, -7.393088, -21.090773, 3.6466384, -7.4172244, -21.11427, 3.6502695, -7.4172244, -21.111706, 4.03517, -7.393088, -21.088425, 4.0392284, -7.393088, -21.090773, 3.6466384, -7.4172244, -21.064928, 4.043073, -7.4172244, -21.067278, 3.6432207, -7.393088, -21.090773, 3.6466384, -7.393088, -21.088425, 4.0392284, -7.4172244, -21.064928, 4.043073, -7.4172244, -21.067278, 3.6432207, -7.441361, -21.088425, 4.0392284, -7.441361, -21.090773, 3.6466384, -7.4172244, -21.067278, 3.6432207, -7.4172244, -21.064928, 4.043073, -7.441361, -21.088425, 4.0392284, -7.441361, -21.090773, 3.6466384, -7.4172244, -21.111706, 4.03517, -7.4172244, -21.11427, 3.6502695, -7.441361, -21.090773, 3.6466384, -7.441361, -21.088425, 4.0392284, -7.4172244, -21.111706, 4.03517, -7.4172244, -21.111706, 4.03517, -7.393088, -21.219572, 4.0757537, -7.393088, -21.088425, 4.0392284, -7.4172244, -21.111706, 4.03517, -7.4172244, -21.224485, 4.0595202, -7.393088, -21.219572, 4.0757537, -7.393088, -21.088425, 4.0392284, -7.4172244, -21.214872, 4.0919867, -7.4172244, -21.064928, 4.043073, -7.393088, -21.088425, 4.0392284, -7.393088, -21.219572, 4.0757537, -7.4172244, -21.214872, 4.0919867, -7.4172244, -21.064928, 4.043073, -7.441361, -21.219572, 4.0757537, -7.441361, -21.088425, 4.0392284, -7.4172244, -21.064928, 4.043073, -7.4172244, -21.214872, 4.0919867, -7.441361, -21.219572, 4.0757537, -7.441361, -21.088425, 4.0392284, -7.4172244, -21.224485, 4.0595202, -7.4172244, -21.111706, 4.03517, -7.441361, -21.088425, 4.0392284, -7.441361, -21.219572, 4.0757537, -7.4172244, -21.224485, 4.0595202, -7.4172244, -21.224485, 4.0595202, -7.393088, -21.355633, 4.0424323, -7.393088, -21.219572, 4.0757537, -7.4172244, -21.224485, 4.0595202, -7.4172244, -21.333633, 4.035811, -7.393088, -21.355633, 4.0424323, -7.393088, -21.219572, 4.0757537, -7.4172244, -21.377848, 4.049054, -7.4172244, -21.214872, 4.0919867, -7.393088, -21.219572, 4.0757537, -7.393088, -21.355633, 4.0424323, -7.4172244, -21.377848, 4.049054, -7.4172244, -21.214872, 4.0919867, -7.441361, -21.355633, 4.0424323, -7.441361, -21.219572, 4.0757537, -7.4172244, -21.214872, 4.0919867, -7.4172244, -21.377848, 4.049054, -7.441361, -21.355633, 4.0424323, -7.441361, -21.219572, 4.0757537, -7.4172244, -21.333633, 4.035811, -7.4172244, -21.224485, 4.0595202, -7.441361, -21.219572, 4.0757537, -7.441361, -21.355633, 4.0424323, -7.4172244, -21.333633, 4.035811, -7.4558854, -21.333632, 4.726582, -7.431749, -21.350079, 4.2887096, -7.431749, -21.355633, 4.7338443, -7.4558854, -21.333632, 4.726582, -7.4558854, -21.328293, 4.2966127, -7.431749, -21.350079, 4.2887096, -7.431749, -21.355633, 4.7338443, -7.4558854, -21.371866, 4.281234, -7.4558854, -21.377846, 4.7411065, -7.431749, -21.355633, 4.7338443, -7.431749, -21.350079, 4.2887096, -7.4558854, -21.371866, 4.281234, -7.4558854, -21.377846, 4.7411065, -7.480022, -21.350079, 4.2887096, -7.480022, -21.355633, 4.7338443, -7.4558854, -21.377846, 4.7411065, -7.4558854, -21.371866, 4.281234, -7.480022, -21.350079, 4.2887096, -7.480022, -21.355633, 4.7338443, -7.4558854, -21.328293, 4.2966127, -7.4558854, -21.333632, 4.726582, -7.480022, -21.355633, 4.7338443, -7.480022, -21.350079, 4.2887096, -7.4558854, -21.328293, 4.2966127, -7.4558854, -21.328293, 4.2966127, -7.431749, -21.208893, 4.272262, -7.431749, -21.350079, 4.2887096, -7.4558854, -21.328293, 4.2966127, -7.4558854, -21.214659, 4.2899904, -7.431749, -21.208893, 4.272262, -7.431749, -21.350079, 4.2887096, -7.4558854, -21.203339, 4.254534, -7.4558854, -21.371866, 4.281234, -7.431749, -21.350079, 4.2887096, -7.431749, -21.208893, 4.272262, -7.4558854, -21.203339, 4.254534, -7.4558854, -21.371866, 4.281234, -7.480022, -21.208893, 4.272262, -7.480022, -21.350079, 4.2887096, -7.4558854, -21.371866, 4.281234, -7.4558854, -21.203339, 4.254534, -7.480022, -21.208893, 4.272262, -7.480022, -21.350079, 4.2887096, -7.4558854, -21.214659, 4.2899904, -7.4558854, -21.328293, 4.2966127, -7.480022, -21.350079, 4.2887096, -7.480022, -21.208893, 4.272262, -7.4558854, -21.214659, 4.2899904, -7.4558854, -21.214659, 4.2899904, -7.431749, -21.090773, 4.2996025, -7.431749, -21.208893, 4.272262, -7.4558854, -21.214659, 4.2899904, -7.4558854, -21.11427, 4.3034472, -7.431749, -21.090773, 4.2996025, -7.431749, -21.208893, 4.272262, -7.4558854, -21.067278, 4.295544, -7.4558854, -21.203339, 4.254534, -7.431749, -21.208893, 4.272262, -7.431749, -21.090773, 4.2996025, -7.4558854, -21.067278, 4.295544, -7.4558854, -21.203339, 4.254534, -7.480022, -21.090773, 4.2996025, -7.480022, -21.208893, 4.272262, -7.4558854, -21.203339, 4.254534, -7.4558854, -21.067278, 4.295544, -7.480022, -21.090773, 4.2996025, -7.480022, -21.208893, 4.272262, -7.4558854, -21.11427, 4.3034472, -7.4558854, -21.214659, 4.2899904, -7.480022, -21.208893, 4.272262, -7.480022, -21.090773, 4.2996025, -7.4558854, -21.11427, 4.3034472, -7.4558854, -21.11427, 4.3034472, -7.431749, -21.088423, 4.730213, -7.431749, -21.090773, 4.2996025, -7.4558854, -21.11427, 4.3034472, -7.4558854, -21.111706, 4.7259407, -7.431749, -21.088423, 4.730213, -7.4558854, -21.111706, 4.7259407, -7.431749, -21.219572, 4.770369, -7.431749, -21.088423, 4.730213, -7.4558854, -21.111706, 4.7259407, -7.4558854, -21.224485, 4.7526407, -7.431749, -21.219572, 4.770369, -7.431749, -21.090773, 4.2996025, -7.4558854, -21.064928, 4.7346983, -7.4558854, -21.067278, 4.295544, -7.431749, -21.090773, 4.2996025, -7.431749, -21.088423, 4.730213, -7.4558854, -21.064928, 4.7346983, -7.431749, -21.088423, 4.730213, -7.4558854, -21.214872, 4.788311, -7.4558854, -21.064928, 4.7346983, -7.431749, -21.088423, 4.730213, -7.431749, -21.219572, 4.770369, -7.4558854, -21.214872, 4.788311, -7.4558854, -21.067278, 4.295544, -7.480022, -21.088423, 4.730213, -7.480022, -21.090773, 4.2996025, -7.4558854, -21.067278, 4.295544, -7.4558854, -21.064928, 4.7346983, -7.480022, -21.088423, 4.730213, -7.4558854, -21.064928, 4.7346983, -7.480022, -21.219572, 4.770369, -7.480022, -21.088423, 4.730213, -7.4558854, -21.064928, 4.7346983, -7.4558854, -21.214872, 4.788311, -7.480022, -21.219572, 4.770369, -7.480022, -21.090773, 4.2996025, -7.4558854, -21.111706, 4.7259407, -7.4558854, -21.11427, 4.3034472, -7.480022, -21.090773, 4.2996025, -7.480022, -21.088423, 4.730213, -7.4558854, -21.111706, 4.7259407, -7.480022, -21.088423, 4.730213, -7.4558854, -21.224485, 4.7526407, -7.4558854, -21.111706, 4.7259407, -7.480022, -21.088423, 4.730213, -7.480022, -21.219572, 4.770369, -7.4558854, -21.224485, 4.7526407, -7.4558854, -21.224485, 4.7526407, -7.431749, -21.355633, 4.7338443, -7.431749, -21.219572, 4.770369, -7.4558854, -21.224485, 4.7526407, -7.4558854, -21.333632, 4.726582, -7.431749, -21.355633, 4.7338443, -7.431749, -21.219572, 4.770369, -7.4558854, -21.377846, 4.7411065, -7.4558854, -21.214872, 4.788311, -7.431749, -21.219572, 4.770369, -7.431749, -21.355633, 4.7338443, -7.4558854, -21.377846, 4.7411065, -7.4558854, -21.214872, 4.788311, -7.480022, -21.355633, 4.7338443, -7.480022, -21.219572, 4.770369, -7.4558854, -21.214872, 4.788311, -7.4558854, -21.377846, 4.7411065, -7.480022, -21.355633, 4.7338443, -7.480022, -21.219572, 4.770369, -7.4558854, -21.333632, 4.726582, -7.4558854, -21.224485, 4.7526407, -7.480022, -21.219572, 4.770369, -7.480022, -21.355633, 4.7338443, -7.4558854, -21.333632, 4.726582, -3.7589583, -21.320816, 4.7411065, -3.7375984, -21.33534, 4.2274075, -3.7375984, -21.340467, 4.7496505, -3.7589583, -21.320816, 4.7411065, -3.7589583, -21.316118, 4.236378, -3.7375984, -21.33534, 4.2274075, -3.7589583, -21.316118, 4.236378, -3.7375984, -21.210388, 4.2079697, -3.7375984, -21.33534, 4.2274075, -3.7589583, -21.316118, 4.236378, -3.7589583, -21.215513, 4.2284746, -3.7375984, -21.210388, 4.2079697, -3.7375984, -21.340467, 4.7496505, -3.7589583, -21.354778, 4.2184362, -3.7589583, -21.360119, 4.758194, -3.7375984, -21.340467, 4.7496505, -3.7375984, -21.33534, 4.2274075, -3.7589583, -21.354778, 4.2184362, -3.7375984, -21.33534, 4.2274075, -3.7589583, -21.205261, 4.1870375, -3.7589583, -21.354778, 4.2184362, -3.7375984, -21.33534, 4.2274075, -3.7375984, -21.210388, 4.2079697, -3.7589583, -21.205261, 4.1870375, -3.7589583, -21.360119, 4.758194, -3.7803178, -21.33534, 4.2274075, -3.7803178, -21.340467, 4.7496505, -3.7589583, -21.360119, 4.758194, -3.7589583, -21.354778, 4.2184362, -3.7803178, -21.33534, 4.2274075, -3.7589583, -21.354778, 4.2184362, -3.7803178, -21.210388, 4.2079697, -3.7803178, -21.33534, 4.2274075, -3.7589583, -21.354778, 4.2184362, -3.7589583, -21.205261, 4.1870375, -3.7803178, -21.210388, 4.2079697, -3.7803178, -21.340467, 4.7496505, -3.7589583, -21.316118, 4.236378, -3.7589583, -21.320816, 4.7411065, -3.7803178, -21.340467, 4.7496505, -3.7803178, -21.33534, 4.2274075, -3.7589583, -21.316118, 4.236378, -3.7803178, -21.33534, 4.2274075, -3.7589583, -21.215513, 4.2284746, -3.7589583, -21.316118, 4.236378, -3.7803178, -21.33534, 4.2274075, -3.7803178, -21.210388, 4.2079697, -3.7589583, -21.215513, 4.2284746, -3.7589583, -21.215513, 4.2284746, -3.7375984, -21.10551, 4.2400093, -3.7375984, -21.210388, 4.2079697, -3.7589583, -21.215513, 4.2284746, -3.7589583, -21.126444, 4.2444944, -3.7375984, -21.10551, 4.2400093, -3.7589583, -21.126444, 4.2444944, -3.7375984, -21.103588, 4.7453785, -3.7375984, -21.10551, 4.2400093, -3.7589583, -21.126444, 4.2444944, -3.7589583, -21.124308, 4.740252, -3.7375984, -21.103588, 4.7453785, -3.7589583, -21.124308, 4.740252, -3.7375984, -21.219786, 4.7925835, -3.7375984, -21.103588, 4.7453785, -3.7589583, -21.124308, 4.740252, -3.7589583, -21.224058, 4.7716503, -3.7375984, -21.219786, 4.7925835, -3.7375984, -21.210388, 4.2079697, -3.7589583, -21.084793, 4.23531, -3.7589583, -21.205261, 4.1870375, -3.7375984, -21.210388, 4.2079697, -3.7375984, -21.10551, 4.2400093, -3.7589583, -21.084793, 4.23531, -3.7375984, -21.10551, 4.2400093, -3.7589583, -21.082657, 4.7505045, -3.7589583, -21.084793, 4.23531, -3.7375984, -21.10551, 4.2400093, -3.7375984, -21.103588, 4.7453785, -3.7589583, -21.082657, 4.7505045, -3.7375984, -21.103588, 4.7453785, -3.7589583, -21.215513, 4.8135157, -3.7589583, -21.082657, 4.7505045, -3.7375984, -21.103588, 4.7453785, -3.7375984, -21.219786, 4.7925835, -3.7589583, -21.215513, 4.8135157, -3.7589583, -21.205261, 4.1870375, -3.7803178, -21.10551, 4.2400093, -3.7803178, -21.210388, 4.2079697, -3.7589583, -21.205261, 4.1870375, -3.7589583, -21.084793, 4.23531, -3.7803178, -21.10551, 4.2400093, -3.7803178, -21.210388, 4.2079697, -3.7589583, -21.126444, 4.2444944, -3.7589583, -21.215513, 4.2284746, -3.7803178, -21.210388, 4.2079697, -3.7803178, -21.10551, 4.2400093, -3.7589583, -21.126444, 4.2444944, -3.7803178, -21.10551, 4.2400093, -3.7589583, -21.124308, 4.740252, -3.7589583, -21.126444, 4.2444944, -3.7803178, -21.10551, 4.2400093, -3.7803178, -21.103588, 4.7453785, -3.7589583, -21.124308, 4.740252, -3.7803178, -21.103588, 4.7453785, -3.7589583, -21.224058, 4.7716503, -3.7589583, -21.124308, 4.740252, -3.7803178, -21.103588, 4.7453785, -3.7803178, -21.219786, 4.7925835, -3.7589583, -21.224058, 4.7716503, -3.7589583, -21.084793, 4.23531, -3.7803178, -21.103588, 4.7453785, -3.7803178, -21.10551, 4.2400093, -3.7589583, -21.084793, 4.23531, -3.7589583, -21.082657, 4.7505045, -3.7803178, -21.103588, 4.7453785, -3.7589583, -21.082657, 4.7505045, -3.7803178, -21.219786, 4.7925835, -3.7803178, -21.103588, 4.7453785, -3.7589583, -21.082657, 4.7505045, -3.7589583, -21.215513, 4.8135157, -3.7803178, -21.219786, 4.7925835, -3.7589583, -21.224058, 4.7716503, -3.7375984, -21.340467, 4.7496505, -3.7375984, -21.219786, 4.7925835, -3.7589583, -21.224058, 4.7716503, -3.7589583, -21.320816, 4.7411065, -3.7375984, -21.340467, 4.7496505, -3.7375984, -21.219786, 4.7925835, -3.7589583, -21.360119, 4.758194, -3.7589583, -21.215513, 4.8135157, -3.7375984, -21.219786, 4.7925835, -3.7375984, -21.340467, 4.7496505, -3.7589583, -21.360119, 4.758194, -3.7589583, -21.215513, 4.8135157, -3.7803178, -21.340467, 4.7496505, -3.7803178, -21.219786, 4.7925835, -3.7589583, -21.215513, 4.8135157, -3.7589583, -21.360119, 4.758194, -3.7803178, -21.340467, 4.7496505, -3.7803178, -21.219786, 4.7925835, -3.7589583, -21.320816, 4.7411065, -3.7589583, -21.224058, 4.7716503, -3.7803178, -21.219786, 4.7925835, -3.7803178, -21.340467, 4.7496505, -3.7589583, -21.320816, 4.7411065, -3.7589583, -21.320816, 5.3611774, -3.7375984, -21.33534, 4.8476915, -3.7375984, -21.340467, 5.3697214, -3.7589583, -21.320816, 5.3611774, -3.7589583, -21.316116, 4.8566623, -3.7375984, -21.33534, 4.8476915, -3.7589583, -21.316116, 4.8566623, -3.7375984, -21.210386, 4.82804, -3.7375984, -21.33534, 4.8476915, -3.7589583, -21.316116, 4.8566623, -3.7589583, -21.215513, 4.8487587, -3.7375984, -21.210386, 4.82804, -3.7375984, -21.340467, 5.3697214, -3.7589583, -21.354778, 4.8387203, -3.7589583, -21.360119, 5.378265, -3.7375984, -21.340467, 5.3697214, -3.7375984, -21.33534, 4.8476915, -3.7589583, -21.354778, 4.8387203, -3.7375984, -21.33534, 4.8476915, -3.7589583, -21.205261, 4.8073215, -3.7589583, -21.354778, 4.8387203, -3.7375984, -21.33534, 4.8476915, -3.7375984, -21.210386, 4.82804, -3.7589583, -21.205261, 4.8073215, -3.7589583, -21.360119, 5.378265, -3.7803178, -21.33534, 4.8476915, -3.7803178, -21.340467, 5.3697214, -3.7589583, -21.360119, 5.378265, -3.7589583, -21.354778, 4.8387203, -3.7803178, -21.33534, 4.8476915, -3.7589583, -21.354778, 4.8387203, -3.7803178, -21.210386, 4.82804, -3.7803178, -21.33534, 4.8476915, -3.7589583, -21.354778, 4.8387203, -3.7589583, -21.205261, 4.8073215, -3.7803178, -21.210386, 4.82804, -3.7803178, -21.340467, 5.3697214, -3.7589583, -21.316116, 4.8566623, -3.7589583, -21.320816, 5.3611774, -3.7803178, -21.340467, 5.3697214, -3.7803178, -21.33534, 4.8476915, -3.7589583, -21.316116, 4.8566623, -3.7803178, -21.33534, 4.8476915, -3.7589583, -21.215513, 4.8487587, -3.7589583, -21.316116, 4.8566623, -3.7803178, -21.33534, 4.8476915, -3.7803178, -21.210386, 4.82804, -3.7589583, -21.215513, 4.8487587, -3.7589583, -21.215513, 4.8487587, -3.7375984, -21.10551, 4.8602934, -3.7375984, -21.210386, 4.82804, -3.7589583, -21.215513, 4.8487587, -3.7589583, -21.126444, 4.8647785, -3.7375984, -21.10551, 4.8602934, -3.7589583, -21.126444, 4.8647785, -3.7375984, -21.103588, 5.3656626, -3.7375984, -21.10551, 4.8602934, -3.7589583, -21.126444, 4.8647785, -3.7589583, -21.124308, 5.3605366, -3.7375984, -21.103588, 5.3656626, -3.7589583, -21.124308, 5.3605366, -3.7375984, -21.219786, 5.412654, -3.7375984, -21.103588, 5.3656626, -3.7589583, -21.124308, 5.3605366, -3.7589583, -21.224056, 5.3917212, -3.7375984, -21.219786, 5.412654, -3.7375984, -21.210386, 4.82804, -3.7589583, -21.084793, 4.855594, -3.7589583, -21.205261, 4.8073215, -3.7375984, -21.210386, 4.82804, -3.7375984, -21.10551, 4.8602934, -3.7589583, -21.084793, 4.855594, -3.7375984, -21.10551, 4.8602934, -3.7589583, -21.082657, 5.370575, -3.7589583, -21.084793, 4.855594, -3.7375984, -21.10551, 4.8602934, -3.7375984, -21.103588, 5.3656626, -3.7589583, -21.082657, 5.370575, -3.7375984, -21.103588, 5.3656626, -3.7589583, -21.215513, 5.433586, -3.7589583, -21.082657, 5.370575, -3.7375984, -21.103588, 5.3656626, -3.7375984, -21.219786, 5.412654, -3.7589583, -21.215513, 5.433586, -3.7589583, -21.205261, 4.8073215, -3.7803178, -21.10551, 4.8602934, -3.7803178, -21.210386, 4.82804, -3.7589583, -21.205261, 4.8073215, -3.7589583, -21.084793, 4.855594, -3.7803178, -21.10551, 4.8602934, -3.7803178, -21.210386, 4.82804, -3.7589583, -21.126444, 4.8647785, -3.7589583, -21.215513, 4.8487587, -3.7803178, -21.210386, 4.82804, -3.7803178, -21.10551, 4.8602934, -3.7589583, -21.126444, 4.8647785, -3.7803178, -21.10551, 4.8602934, -3.7589583, -21.124308, 5.3605366, -3.7589583, -21.126444, 4.8647785, -3.7803178, -21.10551, 4.8602934, -3.7803178, -21.103588, 5.3656626, -3.7589583, -21.124308, 5.3605366, -3.7803178, -21.103588, 5.3656626, -3.7589583, -21.224056, 5.3917212, -3.7589583, -21.124308, 5.3605366, -3.7803178, -21.103588, 5.3656626, -3.7803178, -21.219786, 5.412654, -3.7589583, -21.224056, 5.3917212, -3.7589583, -21.084793, 4.855594, -3.7803178, -21.103588, 5.3656626, -3.7803178, -21.10551, 4.8602934, -3.7589583, -21.084793, 4.855594, -3.7589583, -21.082657, 5.370575, -3.7803178, -21.103588, 5.3656626, -3.7589583, -21.082657, 5.370575, -3.7803178, -21.219786, 5.412654, -3.7803178, -21.103588, 5.3656626, -3.7589583, -21.082657, 5.370575, -3.7589583, -21.215513, 5.433586, -3.7803178, -21.219786, 5.412654, -3.7589583, -21.224056, 5.3917212, -3.7375984, -21.340467, 5.3697214, -3.7375984, -21.219786, 5.412654, -3.7589583, -21.224056, 5.3917212, -3.7589583, -21.320816, 5.3611774, -3.7375984, -21.340467, 5.3697214, -3.7375984, -21.219786, 5.412654, -3.7589583, -21.360119, 5.378265, -3.7589583, -21.215513, 5.433586, -3.7375984, -21.219786, 5.412654, -3.7375984, -21.340467, 5.3697214, -3.7589583, -21.360119, 5.378265, -3.7589583, -21.215513, 5.433586, -3.7803178, -21.340467, 5.3697214, -3.7803178, -21.219786, 5.412654, -3.7589583, -21.215513, 5.433586, -3.7589583, -21.360119, 5.378265, -3.7803178, -21.340467, 5.3697214, -3.7803178, -21.219786, 5.412654, -3.7589583, -21.320816, 5.3611774, -3.7589583, -21.224056, 5.3917212, -3.7803178, -21.219786, 5.412654, -3.7803178, -21.340467, 5.3697214, -3.7589583, -21.320816, 5.3611774, -7.3817677, -21.333632, 5.409023, -7.357631, -21.350079, 4.8643517, -7.357631, -21.355633, 5.418207, -7.3817677, -21.333632, 5.409023, -7.3817677, -21.328293, 4.8739634, -7.357631, -21.350079, 4.8643517, -7.3817677, -21.328293, 4.8739634, -7.357631, -21.208893, 4.8436327, -7.357631, -21.350079, 4.8643517, -7.3817677, -21.328293, 4.8739634, -7.3817677, -21.214659, 4.8654194, -7.357631, -21.208893, 4.8436327, -7.357631, -21.355633, 5.418207, -7.3817677, -21.371866, 4.85474, -7.3817677, -21.377846, 5.4271784, -7.357631, -21.355633, 5.418207, -7.357631, -21.350079, 4.8643517, -7.3817677, -21.371866, 4.85474, -7.357631, -21.350079, 4.8643517, -7.3817677, -21.203339, 4.8216324, -7.3817677, -21.371866, 4.85474, -7.357631, -21.350079, 4.8643517, -7.357631, -21.208893, 4.8436327, -7.3817677, -21.203339, 4.8216324, -7.3817677, -21.377846, 5.4271784, -7.4059043, -21.350079, 4.8643517, -7.4059043, -21.355633, 5.418207, -7.3817677, -21.377846, 5.4271784, -7.3817677, -21.371866, 4.85474, -7.4059043, -21.350079, 4.8643517, -7.3817677, -21.371866, 4.85474, -7.4059043, -21.208893, 4.8436327, -7.4059043, -21.350079, 4.8643517, -7.3817677, -21.371866, 4.85474, -7.3817677, -21.203339, 4.8216324, -7.4059043, -21.208893, 4.8436327, -7.4059043, -21.355633, 5.418207, -7.3817677, -21.328293, 4.8739634, -7.3817677, -21.333632, 5.409023, -7.4059043, -21.355633, 5.418207, -7.4059043, -21.350079, 4.8643517, -7.3817677, -21.328293, 4.8739634, -7.4059043, -21.350079, 4.8643517, -7.3817677, -21.214659, 4.8654194, -7.3817677, -21.328293, 4.8739634, -7.4059043, -21.350079, 4.8643517, -7.4059043, -21.208893, 4.8436327, -7.3817677, -21.214659, 4.8654194, -7.3817677, -21.214659, 4.8654194, -7.357631, -21.090773, 4.8775945, -7.357631, -21.208893, 4.8436327, -7.3817677, -21.214659, 4.8654194, -7.3817677, -21.11427, 4.8825073, -7.357631, -21.090773, 4.8775945, -7.3817677, -21.11427, 4.8825073, -7.357631, -21.088423, 5.4137216, -7.357631, -21.090773, 4.8775945, -7.3817677, -21.11427, 4.8825073, -7.3817677, -21.111706, 5.4083815, -7.357631, -21.088423, 5.4137216, -7.3817677, -21.111706, 5.4083815, -7.357631, -21.219572, 5.463703, -7.357631, -21.088423, 5.4137216, -7.3817677, -21.111706, 5.4083815, -7.3817677, -21.224483, 5.441489, -7.357631, -21.219572, 5.463703, -7.357631, -21.208893, 4.8436327, -7.3817677, -21.067278, 4.8726816, -7.3817677, -21.203339, 4.8216324, -7.357631, -21.208893, 4.8436327, -7.357631, -21.090773, 4.8775945, -7.3817677, -21.067278, 4.8726816, -7.357631, -21.090773, 4.8775945, -7.3817677, -21.064928, 5.4190617, -7.3817677, -21.067278, 4.8726816, -7.357631, -21.090773, 4.8775945, -7.357631, -21.088423, 5.4137216, -7.3817677, -21.064928, 5.4190617, -7.357631, -21.088423, 5.4137216, -7.3817677, -21.214872, 5.485917, -7.3817677, -21.064928, 5.4190617, -7.357631, -21.088423, 5.4137216, -7.357631, -21.219572, 5.463703, -7.3817677, -21.214872, 5.485917, -7.3817677, -21.203339, 4.8216324, -7.4059043, -21.090773, 4.8775945, -7.4059043, -21.208893, 4.8436327, -7.3817677, -21.203339, 4.8216324, -7.3817677, -21.067278, 4.8726816, -7.4059043, -21.090773, 4.8775945, -7.4059043, -21.208893, 4.8436327, -7.3817677, -21.11427, 4.8825073, -7.3817677, -21.214659, 4.8654194, -7.4059043, -21.208893, 4.8436327, -7.4059043, -21.090773, 4.8775945, -7.3817677, -21.11427, 4.8825073, -7.4059043, -21.090773, 4.8775945, -7.3817677, -21.111706, 5.4083815, -7.3817677, -21.11427, 4.8825073, -7.4059043, -21.090773, 4.8775945, -7.4059043, -21.088423, 5.4137216, -7.3817677, -21.111706, 5.4083815, -7.4059043, -21.088423, 5.4137216, -7.3817677, -21.224483, 5.441489, -7.3817677, -21.111706, 5.4083815, -7.4059043, -21.088423, 5.4137216, -7.4059043, -21.219572, 5.463703, -7.3817677, -21.224483, 5.441489, -7.3817677, -21.067278, 4.8726816, -7.4059043, -21.088423, 5.4137216, -7.4059043, -21.090773, 4.8775945, -7.3817677, -21.067278, 4.8726816, -7.3817677, -21.064928, 5.4190617, -7.4059043, -21.088423, 5.4137216, -7.3817677, -21.064928, 5.4190617, -7.4059043, -21.219572, 5.463703, -7.4059043, -21.088423, 5.4137216, -7.3817677, -21.064928, 5.4190617, -7.3817677, -21.214872, 5.485917, -7.4059043, -21.219572, 5.463703, -7.3817677, -21.224483, 5.441489, -7.357631, -21.355633, 5.418207, -7.357631, -21.219572, 5.463703, -7.3817677, -21.224483, 5.441489, -7.3817677, -21.333632, 5.409023, -7.357631, -21.355633, 5.418207, -7.357631, -21.219572, 5.463703, -7.3817677, -21.377846, 5.4271784, -7.3817677, -21.214872, 5.485917, -7.357631, -21.219572, 5.463703, -7.357631, -21.355633, 5.418207, -7.3817677, -21.377846, 5.4271784, -7.3817677, -21.214872, 5.485917, -7.4059043, -21.355633, 5.418207, -7.4059043, -21.219572, 5.463703, -7.3817677, -21.214872, 5.485917, -7.3817677, -21.377846, 5.4271784, -7.4059043, -21.355633, 5.418207, -7.4059043, -21.219572, 5.463703, -7.3817677, -21.333632, 5.409023, -7.3817677, -21.224483, 5.441489, -7.4059043, -21.219572, 5.463703, -7.4059043, -21.355633, 5.418207, -7.3817677, -21.333632, 5.409023, 16.86181, -22.803505, -1.1805243, 12.459326, -22.631578, -0.992887, 16.845665, -22.437832, -1.1752877, 16.86181, -22.803505, -1.1805243, 12.4754715, -22.996815, -0.99855995, 12.459326, -22.631578, -0.992887, 21.372076, -22.244087, -0.9867778, 16.86181, -22.803505, -1.1805243, 16.845665, -22.437832, -1.1752877, 21.372076, -22.244087, -0.9867778, 21.38822, -22.609324, -0.9924512, 16.86181, -22.803505, -1.1805243, 16.86181, -22.78867, -2.160163, 12.4754715, -22.996815, -0.99855995, 16.86181, -22.803505, -1.1805243, 16.86181, -22.78867, -2.160163, 12.4754715, -22.984161, -1.8285255, 12.4754715, -22.996815, -0.99855995, 16.86181, -22.803505, -1.1805243, 21.38822, -22.592306, -2.12569, 16.86181, -22.78867, -2.160163, 16.86181, -22.803505, -1.1805243, 21.38822, -22.601032, -1.5496888, 21.38822, -22.592306, -2.12569, 21.38822, -22.601032, -1.5496888, 16.86181, -22.803505, -1.1805243, 21.38822, -22.609324, -0.9924512, 12.4754715, -22.984161, -1.8285255, 12.459326, -22.631578, -0.992887, 12.4754715, -22.996815, -0.99855995, 12.4754715, -22.984161, -1.8285255, 12.459326, -22.618923, -1.8228531, 12.459326, -22.631578, -0.992887, 21.372076, -22.236668, -1.4776883, 21.20582, -22.351868, -1.5763068, 21.38822, -22.601032, -1.5496888, 16.845665, -22.426924, -1.9096894, 16.849592, -22.514633, -2.155799, 17.977158, -22.374123, -2.1461992, 16.845665, -22.426924, -1.9096894, 15.749081, -22.472305, -2.0715804, 16.849592, -22.514633, -2.155799, 16.845665, -22.426924, -1.9096894, 12.459326, -22.618923, -1.8228531, 15.749081, -22.472305, -2.0715804, 16.845665, -22.426924, -1.9096894, 12.459326, -22.631578, -0.992887, 12.459326, -22.618923, -1.8228531, 12.459326, -22.631578, -0.992887, 16.845665, -22.426924, -1.9096894, 16.845665, -22.437832, -1.1752877, 17.977158, -22.374123, -2.1461992, 16.845665, -22.437832, -1.1752877, 16.845665, -22.426924, -1.9096894, 16.845665, -22.437832, -1.1752877, 21.372076, -22.236668, -1.4776883, 21.372076, -22.244087, -0.9867778, 16.845665, -22.437832, -1.1752877, 20.594036, -22.268959, -1.5780525, 21.372076, -22.236668, -1.4776883, 17.977158, -22.374123, -2.1461992, 20.594036, -22.268959, -1.5780525, 16.845665, -22.437832, -1.1752877, 21.20582, -22.351868, -1.5763068, 21.372076, -22.236668, -1.4776883, 20.594036, -22.268959, -1.5780525, 21.372076, -22.232304, -1.7539072, 21.20582, -22.351868, -1.5763068, 20.594036, -22.268959, -1.5780525, 20.594036, -22.268959, -1.5780525, 17.977158, -22.374123, -2.1461992, 21.372076, -22.232304, -1.7539072, 21.372076, -22.232304, -1.7539072, 17.977158, -22.374123, -2.1461992, 21.37251, -22.227068, -2.1200175, 21.38822, -22.609324, -0.9924512, 21.372076, -22.236668, -1.4776883, 21.38822, -22.601032, -1.5496888, 21.38822, -22.609324, -0.9924512, 21.372076, -22.244087, -0.9867778, 21.372076, -22.236668, -1.4776883, 21.372076, -22.232304, -1.7539072, 21.38822, -22.592306, -2.12569, 21.38822, -22.601032, -1.5496888, 21.372076, -22.232304, -1.7539072, 21.37251, -22.227068, -2.1200175, 21.38822, -22.592306, -2.12569, 21.20582, -22.351868, -1.5763068, 21.372076, -22.232304, -1.7539072, 21.38822, -22.601032, -1.5496888, 21.37251, -22.227068, -2.1200175, 16.86181, -22.78867, -2.160163, 21.38822, -22.592306, -2.12569, 16.86181, -22.78867, -2.160163, 21.37251, -22.227068, -2.1200175, 16.849592, -22.514633, -2.155799, 16.849592, -22.514633, -2.155799, 21.37251, -22.227068, -2.1200175, 17.977158, -22.374123, -2.1461992, 16.849592, -22.514633, -2.155799, 12.459326, -22.618923, -1.8228531, 16.86181, -22.78867, -2.160163, 16.86181, -22.78867, -2.160163, 12.459326, -22.618923, -1.8228531, 12.4754715, -22.984161, -1.8285255, 12.459326, -22.618923, -1.8228531, 16.849592, -22.514633, -2.155799, 15.749081, -22.472305, -2.0715804, 13.651039, -22.54256, -3.436966, 12.76609, -22.946634, -3.462275, 13.666748, -22.907797, -3.4426389, 13.651039, -22.54256, -3.436966, 12.749945, -22.581396, -3.4570384, 12.76609, -22.946634, -3.462275, 13.666748, -22.907797, -3.4426389, 21.08582, -22.31085, -3.4526749, 13.651039, -22.54256, -3.436966, 13.651039, -22.54256, -3.436966, 21.08582, -22.31085, -3.4526749, 19.224287, -22.300377, -3.4474387, 21.08582, -22.31085, -3.4526749, 13.666748, -22.907797, -3.4426389, 21.0976, -22.584887, -3.4566026, 12.749945, -22.596233, -2.4743457, 13.651039, -22.54256, -3.436966, 13.651039, -22.557396, -2.4507813, 19.224287, -22.300377, -3.4474387, 13.651039, -22.557396, -2.4507813, 13.651039, -22.54256, -3.436966, 19.224287, -22.300377, -3.4474387, 21.081892, -22.231869, -2.6214004, 13.651039, -22.557396, -2.4507813, 21.081892, -22.231869, -2.6214004, 19.224287, -22.300377, -3.4474387, 21.081892, -22.222704, -3.2436562, 21.081892, -22.222704, -3.2436562, 19.224287, -22.300377, -3.4474387, 21.08582, -22.31085, -3.4526749, 13.651039, -22.54256, -3.436966, 12.749945, -22.596233, -2.4743457, 13.230383, -22.567432, -2.9914374, 13.230383, -22.567432, -2.9914374, 12.749945, -22.596233, -2.4743457, 12.749945, -22.590124, -2.8701282, 12.749945, -22.590124, -2.8701282, 12.838528, -22.724524, -2.981401, 13.230383, -22.567432, -2.9914374, 12.838528, -22.724524, -2.981401, 12.749945, -22.586197, -3.144165, 13.230383, -22.567432, -2.9914374, 13.651039, -22.54256, -3.436966, 13.230383, -22.567432, -2.9914374, 12.749945, -22.586197, -3.144165, 13.651039, -22.54256, -3.436966, 12.749945, -22.586197, -3.144165, 12.749945, -22.581396, -3.4570384, 13.666748, -22.923069, -2.4560184, 12.749945, -22.596233, -2.4743457, 13.651039, -22.557396, -2.4507813, 13.666748, -22.923069, -2.4560184, 12.76609, -22.96147, -2.4795818, 12.749945, -22.596233, -2.4743457, 21.081892, -22.231869, -2.6214004, 13.666748, -22.923069, -2.4560184, 13.651039, -22.557396, -2.4507813, 21.081892, -22.231869, -2.6214004, 21.0976, -22.597105, -2.6270728, 13.666748, -22.923069, -2.4560184, 12.76609, -22.946634, -3.462275, 13.666748, -22.923069, -2.4560184, 13.666748, -22.907797, -3.4426389, 13.666748, -22.923069, -2.4560184, 12.76609, -22.946634, -3.462275, 12.76609, -22.954924, -2.931219, 13.666748, -22.923069, -2.4560184, 12.76609, -22.954924, -2.931219, 12.76609, -22.96147, -2.4795818, 21.0976, -22.597105, -2.6270728, 13.666748, -22.907797, -3.4426389, 13.666748, -22.923069, -2.4560184, 21.0976, -22.597105, -2.6270728, 21.0976, -22.584887, -3.4566026, 13.666748, -22.907797, -3.4426389, 12.749945, -22.586197, -3.144165, 12.838528, -22.724524, -2.981401, 12.76609, -22.954924, -2.931219, 12.838528, -22.724524, -2.981401, 12.749945, -22.590124, -2.8701282, 12.76609, -22.954924, -2.931219, 12.76609, -22.946634, -3.462275, 12.749945, -22.586197, -3.144165, 12.76609, -22.954924, -2.931219, 12.76609, -22.946634, -3.462275, 12.749945, -22.581396, -3.4570384, 12.749945, -22.586197, -3.144165, 12.749945, -22.590124, -2.8701282, 12.76609, -22.96147, -2.4795818, 12.76609, -22.954924, -2.931219, 12.749945, -22.590124, -2.8701282, 12.749945, -22.596233, -2.4743457, 12.76609, -22.96147, -2.4795818, 21.081892, -22.231869, -2.6214004, 21.0976, -22.584887, -3.4566026, 21.0976, -22.597105, -2.6270728, 21.0976, -22.584887, -3.4566026, 21.081892, -22.231869, -2.6214004, 21.08582, -22.31085, -3.4526749, 21.08582, -22.31085, -3.4526749, 21.081892, -22.231869, -2.6214004, 21.081892, -22.222704, -3.2436562, 16.86661, -22.74067, -5.2888975, 12.750381, -22.556524, -5.1012607, 16.850464, -22.375433, -5.2836614, 16.86661, -22.74067, -5.2888975, 12.76609, -22.92176, -5.1069336, 12.750381, -22.556524, -5.1012607, 21.081892, -22.194342, -5.0951514, 16.86661, -22.74067, -5.2888975, 16.850464, -22.375433, -5.2836614, 21.081892, -22.194342, -5.0951514, 21.0976, -22.559578, -5.100825, 16.86661, -22.74067, -5.2888975, 16.86661, -22.725834, -6.2681003, 12.76609, -22.92176, -5.1069336, 16.86661, -22.74067, -5.2888975, 16.86661, -22.725834, -6.2681003, 12.76609, -22.909105, -5.936463, 12.76609, -22.92176, -5.1069336, 16.86661, -22.74067, -5.2888975, 21.0976, -22.54256, -6.2344995, 16.86661, -22.725834, -6.2681003, 16.86661, -22.74067, -5.2888975, 21.0976, -22.551287, -5.658062, 21.0976, -22.54256, -6.2344995, 21.0976, -22.551287, -5.658062, 16.86661, -22.74067, -5.2888975, 21.0976, -22.559578, -5.100825, 12.76609, -22.909105, -5.936463, 12.750381, -22.556524, -5.1012607, 12.76609, -22.92176, -5.1069336, 12.76609, -22.909105, -5.936463, 12.750381, -22.543869, -5.93079, 12.750381, -22.556524, -5.1012607, 21.081892, -22.186922, -5.5856256, 20.926546, -22.301687, -5.6846805, 21.0976, -22.551287, -5.658062, 16.850464, -22.364523, -6.018063, 16.854391, -22.451796, -6.2637362, 17.908213, -22.314777, -6.2541366, 16.850464, -22.364523, -6.018063, 15.825443, -22.406414, -6.179518, 16.854391, -22.451796, -6.2637362, 16.850464, -22.364523, -6.018063, 12.750381, -22.543869, -5.93079, 15.825443, -22.406414, -6.179518, 16.850464, -22.364523, -6.018063, 12.750381, -22.556524, -5.1012607, 12.750381, -22.543869, -5.93079, 12.750381, -22.556524, -5.1012607, 16.850464, -22.364523, -6.018063, 16.850464, -22.375433, -5.2836614, 17.908213, -22.314777, -6.2541366, 16.850464, -22.375433, -5.2836614, 16.850464, -22.364523, -6.018063, 16.850464, -22.375433, -5.2836614, 21.081892, -22.186922, -5.5856256, 21.081892, -22.194342, -5.0951514, 16.850464, -22.375433, -5.2836614, 20.354473, -22.217031, -5.6864257, 21.081892, -22.186922, -5.5856256, 17.908213, -22.314777, -6.2541366, 20.354473, -22.217031, -5.6864257, 16.850464, -22.375433, -5.2836614, 20.926546, -22.301687, -5.6846805, 21.081892, -22.186922, -5.5856256, 20.354473, -22.217031, -5.6864257, 21.081892, -22.182995, -5.862281, 20.926546, -22.301687, -5.6846805, 20.354473, -22.217031, -5.6864257, 20.354473, -22.217031, -5.6864257, 17.908213, -22.314777, -6.2541366, 21.081892, -22.182995, -5.862281, 21.081892, -22.182995, -5.862281, 17.908213, -22.314777, -6.2541366, 21.081892, -22.177322, -6.228827, 21.0976, -22.559578, -5.100825, 21.081892, -22.186922, -5.5856256, 21.0976, -22.551287, -5.658062, 21.0976, -22.559578, -5.100825, 21.081892, -22.194342, -5.0951514, 21.081892, -22.186922, -5.5856256, 21.081892, -22.182995, -5.862281, 21.0976, -22.54256, -6.2344995, 21.0976, -22.551287, -5.658062, 21.081892, -22.182995, -5.862281, 21.081892, -22.177322, -6.228827, 21.0976, -22.54256, -6.2344995, 20.926546, -22.301687, -5.6846805, 21.081892, -22.182995, -5.862281, 21.0976, -22.551287, -5.658062, 21.081892, -22.177322, -6.228827, 16.86661, -22.725834, -6.2681003, 21.0976, -22.54256, -6.2344995, 16.86661, -22.725834, -6.2681003, 21.081892, -22.177322, -6.228827, 16.854391, -22.451796, -6.2637362, 16.854391, -22.451796, -6.2637362, 21.081892, -22.177322, -6.228827, 17.908213, -22.314777, -6.2541366, 16.854391, -22.451796, -6.2637362, 12.750381, -22.543869, -5.93079, 16.86661, -22.725834, -6.2681003, 16.86661, -22.725834, -6.2681003, 12.750381, -22.543869, -5.93079, 12.76609, -22.909105, -5.936463, 12.750381, -22.543869, -5.93079, 16.854391, -22.451796, -6.2637362, 15.825443, -22.406414, -6.179518, 19.724798, -22.63725, -3.9361672, 12.459763, -22.58925, -3.7760215, 19.70909, -22.271578, -3.9304943, 19.724798, -22.63725, -3.9361672, 12.4754715, -22.954489, -3.781694, 12.459763, -22.58925, -3.7760215, 21.37251, -22.199577, -3.9226394, 19.724798, -22.63725, -3.9361672, 19.70909, -22.271578, -3.9304943, 21.37251, -22.199577, -3.9226394, 21.38822, -22.564814, -3.9283128, 19.724798, -22.63725, -3.9361672, 19.724798, -22.621979, -4.944606, 12.4754715, -22.954489, -3.781694, 19.724798, -22.63725, -3.9361672, 19.724798, -22.621979, -4.944606, 12.4754715, -22.939651, -4.764387, 12.4754715, -22.954489, -3.781694, 21.38822, -22.564814, -3.9283128, 19.724798, -22.621979, -4.944606, 19.724798, -22.63725, -3.9361672, 21.38822, -22.564814, -3.9283128, 21.38822, -22.55216, -4.7578416, 19.724798, -22.621979, -4.944606, 21.38822, -22.564814, -3.9283128, 21.37251, -22.186922, -4.752169, 21.38822, -22.55216, -4.7578416, 21.38822, -22.564814, -3.9283128, 21.37251, -22.199577, -3.9226394, 21.37251, -22.186922, -4.752169, 19.70909, -22.260231, -4.6867146, 19.713017, -22.34794, -4.9402423, 20.124945, -22.239286, -4.8926787, 19.70909, -22.260231, -4.6867146, 17.896868, -22.335724, -4.8939877, 19.713017, -22.34794, -4.9402423, 19.70909, -22.260231, -4.6867146, 13.819038, -22.514633, -4.792751, 17.896868, -22.335724, -4.8939877, 20.124945, -22.239286, -4.8926787, 19.70909, -22.271578, -3.9304943, 19.70909, -22.260231, -4.6867146, 20.124945, -22.239286, -4.8926787, 21.37251, -22.199577, -3.9226394, 19.70909, -22.271578, -3.9304943, 21.37251, -22.199577, -3.9226394, 20.124945, -22.239286, -4.8926787, 21.37251, -22.186922, -4.752169, 12.459763, -22.58925, -3.7760215, 19.70909, -22.260231, -4.6867146, 19.70909, -22.271578, -3.9304943, 19.70909, -22.260231, -4.6867146, 12.459763, -22.58925, -3.7760215, 13.819038, -22.514633, -4.792751, 13.819038, -22.514633, -4.792751, 12.459763, -22.58925, -3.7760215, 12.459763, -22.577906, -4.513041, 13.819038, -22.514633, -4.792751, 12.459763, -22.577906, -4.513041, 12.46369, -22.665615, -4.7600236, 21.37251, -22.186922, -4.752169, 19.724798, -22.621979, -4.944606, 21.38822, -22.55216, -4.7578416, 19.724798, -22.621979, -4.944606, 21.37251, -22.186922, -4.752169, 19.713017, -22.34794, -4.9402423, 19.713017, -22.34794, -4.9402423, 21.37251, -22.186922, -4.752169, 20.124945, -22.239286, -4.8926787, 19.713017, -22.34794, -4.9402423, 12.46369, -22.665615, -4.7600236, 19.724798, -22.621979, -4.944606, 19.724798, -22.621979, -4.944606, 12.46369, -22.665615, -4.7600236, 12.4754715, -22.939651, -4.764387, 12.46369, -22.665615, -4.7600236, 19.713017, -22.34794, -4.9402423, 13.819038, -22.514633, -4.792751, 13.819038, -22.514633, -4.792751, 19.713017, -22.34794, -4.9402423, 17.896868, -22.335724, -4.8939877, 12.46369, -22.665615, -4.7600236, 12.4754715, -22.954489, -3.781694, 12.4754715, -22.939651, -4.764387, 12.4754715, -22.954489, -3.781694, 12.46369, -22.665615, -4.7600236, 12.459763, -22.58925, -3.7760215, 12.459763, -22.58925, -3.7760215, 12.46369, -22.665615, -4.7600236, 12.459763, -22.577906, -4.513041, 16.95781, -23.00336, -0.79565, 21.06182, -22.63725, -0.9811053, 16.95781, -22.63725, -0.79565, 16.95781, -23.00336, -0.79565, 21.06182, -23.00336, -0.9811053, 21.06182, -22.63725, -0.9811053, 12.722454, -22.63725, -0.9811053, 16.95781, -23.00336, -0.79565, 16.95781, -22.63725, -0.79565, 12.722454, -22.63725, -0.9811053, 12.722454, -23.00336, -0.9811053, 16.95781, -23.00336, -0.79565, 16.95781, -23.00336, 0.18311548, 21.06182, -23.00336, -0.9811053, 16.95781, -23.00336, -0.79565, 16.95781, -23.00336, 0.18311548, 21.06182, -23.00336, -0.15113974, 21.06182, -23.00336, -0.9811053, 16.95781, -23.00336, -0.79565, 12.722454, -23.00336, 0.15257025, 16.95781, -23.00336, 0.18311548, 16.95781, -23.00336, -0.79565, 12.722454, -23.00336, -0.4238677, 12.722454, -23.00336, 0.15257025, 12.722454, -23.00336, -0.4238677, 16.95781, -23.00336, -0.79565, 12.722454, -23.00336, -0.9811053, 21.06182, -23.00336, -0.15113974, 21.06182, -22.63725, -0.9811053, 21.06182, -23.00336, -0.9811053, 21.06182, -23.00336, -0.15113974, 21.06182, -22.63725, -0.15113974, 21.06182, -22.63725, -0.9811053, 12.722454, -22.63725, -0.49063158, 12.882601, -22.746778, -0.39288568, 12.722454, -23.00336, -0.4238677, 16.95781, -22.63725, -0.06124878, 16.95781, -22.728888, 0.18311548, 15.899189, -22.63725, 0.17569733, 16.95781, -22.63725, -0.06124878, 17.983704, -22.63725, 0.09977007, 16.95781, -22.728888, 0.18311548, 16.95781, -22.63725, -0.06124878, 21.06182, -22.63725, -0.15113974, 17.983704, -22.63725, 0.09977007, 16.95781, -22.63725, -0.06124878, 21.06182, -22.63725, -0.9811053, 21.06182, -22.63725, -0.15113974, 21.06182, -22.63725, -0.9811053, 16.95781, -22.63725, -0.06124878, 16.95781, -22.63725, -0.79565, 15.899189, -22.63725, 0.17569733, 16.95781, -22.63725, -0.79565, 16.95781, -22.63725, -0.06124878, 16.95781, -22.63725, -0.79565, 12.722454, -22.63725, -0.49063158, 12.722454, -22.63725, -0.9811053, 16.95781, -22.63725, -0.79565, 13.450312, -22.63725, -0.39070415, 12.722454, -22.63725, -0.49063158, 15.899189, -22.63725, 0.17569733, 13.450312, -22.63725, -0.39070415, 16.95781, -22.63725, -0.79565, 12.882601, -22.746778, -0.39288568, 12.722454, -22.63725, -0.49063158, 13.450312, -22.63725, -0.39070415, 12.722454, -22.63725, -0.21441269, 12.882601, -22.746778, -0.39288568, 13.450312, -22.63725, -0.39070415, 13.450312, -22.63725, -0.39070415, 15.899189, -22.63725, 0.17569733, 12.722454, -22.63725, -0.21441269, 12.722454, -22.63725, -0.21441269, 15.899189, -22.63725, 0.17569733, 12.722454, -22.63725, 0.15257025, 12.722454, -23.00336, -0.9811053, 12.722454, -22.63725, -0.49063158, 12.722454, -23.00336, -0.4238677, 12.722454, -23.00336, -0.9811053, 12.722454, -22.63725, -0.9811053, 12.722454, -22.63725, -0.49063158, 12.722454, -22.63725, -0.21441269, 12.722454, -23.00336, 0.15257025, 12.722454, -23.00336, -0.4238677, 12.722454, -22.63725, -0.21441269, 12.722454, -22.63725, 0.15257025, 12.722454, -23.00336, 0.15257025, 12.882601, -22.746778, -0.39288568, 12.722454, -22.63725, -0.21441269, 12.722454, -23.00336, -0.4238677, 12.722454, -22.63725, 0.15257025, 16.95781, -23.00336, 0.18311548, 12.722454, -23.00336, 0.15257025, 16.95781, -23.00336, 0.18311548, 12.722454, -22.63725, 0.15257025, 15.899189, -22.63725, 0.17569733, 16.95781, -23.00336, 0.18311548, 15.899189, -22.63725, 0.17569733, 16.95781, -22.728888, 0.18311548, 16.95781, -22.728888, 0.18311548, 21.06182, -23.00336, -0.15113974, 16.95781, -23.00336, 0.18311548, 16.95781, -22.728888, 0.18311548, 21.06182, -22.63725, -0.15113974, 21.06182, -23.00336, -0.15113974, 21.06182, -22.63725, -0.15113974, 16.95781, -22.728888, 0.18311548, 17.983704, -22.63725, 0.09977007, 20.16029, -22.63725, 2.5866122, 21.06182, -23.00336, 2.6058125, 20.16029, -23.00336, 2.5866122, 20.16029, -22.63725, 2.5866122, 21.06182, -22.63725, 2.6058125, 21.06182, -23.00336, 2.6058125, 20.16029, -23.00336, 2.5866122, 14.582241, -22.63725, 2.6010118, 20.16029, -22.63725, 2.5866122, 20.16029, -23.00336, 2.5866122, 12.722454, -22.728888, 2.6058125, 14.582241, -22.63725, 2.6010118, 12.722454, -22.728888, 2.6058125, 20.16029, -23.00336, 2.5866122, 12.722454, -23.00336, 2.6058125, 21.06182, -22.63725, 1.6231189, 20.16029, -22.63725, 2.5866122, 20.16029, -22.63725, 1.6004281, 14.582241, -22.63725, 2.6010118, 20.16029, -22.63725, 1.6004281, 20.16029, -22.63725, 2.5866122, 14.582241, -22.63725, 2.6010118, 12.722454, -22.63725, 1.7762828, 20.16029, -22.63725, 1.6004281, 12.722454, -22.63725, 1.7762828, 14.582241, -22.63725, 2.6010118, 12.722454, -22.63725, 2.398975, 12.722454, -22.63725, 2.398975, 14.582241, -22.63725, 2.6010118, 12.722454, -22.728888, 2.6058125, 20.16029, -22.63725, 2.5866122, 21.06182, -22.63725, 1.6231189, 20.581383, -22.63725, 2.1410842, 20.581383, -22.63725, 2.1410842, 21.06182, -22.63725, 1.6231189, 21.06182, -22.63725, 2.0193381, 21.06182, -22.63725, 2.0193381, 20.979784, -22.776888, 2.128429, 20.581383, -22.63725, 2.1410842, 20.979784, -22.776888, 2.128429, 21.06182, -22.63725, 2.2933755, 20.581383, -22.63725, 2.1410842, 20.16029, -22.63725, 2.5866122, 20.581383, -22.63725, 2.1410842, 21.06182, -22.63725, 2.2933755, 20.16029, -22.63725, 2.5866122, 21.06182, -22.63725, 2.2933755, 21.06182, -22.63725, 2.6058125, 20.16029, -23.00336, 1.6004281, 21.06182, -22.63725, 1.6231189, 20.16029, -22.63725, 1.6004281, 20.16029, -23.00336, 1.6004281, 21.06182, -23.00336, 1.6231189, 21.06182, -22.63725, 1.6231189, 12.722454, -22.63725, 1.7762828, 20.16029, -23.00336, 1.6004281, 20.16029, -22.63725, 1.6004281, 12.722454, -22.63725, 1.7762828, 12.722454, -23.00336, 1.7762828, 20.16029, -23.00336, 1.6004281, 21.06182, -23.00336, 2.6058125, 20.16029, -23.00336, 1.6004281, 20.16029, -23.00336, 2.5866122, 20.16029, -23.00336, 1.6004281, 21.06182, -23.00336, 2.6058125, 21.06182, -23.00336, 2.0747561, 20.16029, -23.00336, 1.6004281, 21.06182, -23.00336, 2.0747561, 21.06182, -23.00336, 1.6231189, 12.722454, -23.00336, 1.7762828, 20.16029, -23.00336, 2.5866122, 20.16029, -23.00336, 1.6004281, 12.722454, -23.00336, 1.7762828, 12.722454, -23.00336, 2.6058125, 20.16029, -23.00336, 2.5866122, 21.06182, -22.63725, 2.2933755, 20.979784, -22.776888, 2.128429, 21.06182, -23.00336, 2.0747561, 20.979784, -22.776888, 2.128429, 21.06182, -22.63725, 2.0193381, 21.06182, -23.00336, 2.0747561, 21.06182, -23.00336, 2.6058125, 21.06182, -22.63725, 2.2933755, 21.06182, -23.00336, 2.0747561, 21.06182, -23.00336, 2.6058125, 21.06182, -22.63725, 2.6058125, 21.06182, -22.63725, 2.2933755, 21.06182, -22.63725, 2.0193381, 21.06182, -23.00336, 1.6231189, 21.06182, -23.00336, 2.0747561, 21.06182, -22.63725, 2.0193381, 21.06182, -22.63725, 1.6231189, 21.06182, -23.00336, 1.6231189, 12.722454, -22.63725, 1.7762828, 12.722454, -23.00336, 2.6058125, 12.722454, -23.00336, 1.7762828, 12.722454, -23.00336, 2.6058125, 12.722454, -22.63725, 1.7762828, 12.722454, -22.63725, 2.398975, 12.722454, -23.00336, 2.6058125, 12.722454, -22.63725, 2.398975, 12.722454, -22.728888, 2.6058125, 20.38851, -22.63725, 3.7669787, 21.352875, -23.00336, 3.786178, 20.38851, -23.00336, 3.7669787, 20.38851, -22.63725, 3.7669787, 21.352875, -22.63725, 3.786178, 21.352875, -23.00336, 3.786178, 20.38851, -23.00336, 3.7669787, 14.420786, -22.63725, 3.7813792, 20.38851, -22.63725, 3.7669787, 20.38851, -23.00336, 3.7669787, 12.431835, -22.728888, 3.786178, 14.420786, -22.63725, 3.7813792, 12.431835, -22.728888, 3.786178, 20.38851, -23.00336, 3.7669787, 12.431835, -23.00336, 3.786178, 21.352875, -22.63725, 2.8034854, 20.38851, -22.63725, 3.7669787, 20.38851, -22.63725, 2.7803578, 14.420786, -22.63725, 3.7813792, 20.38851, -22.63725, 2.7803578, 20.38851, -22.63725, 3.7669787, 14.420786, -22.63725, 3.7813792, 12.431835, -22.63725, 2.9562135, 20.38851, -22.63725, 2.7803578, 12.431835, -22.63725, 2.9562135, 14.420786, -22.63725, 3.7813792, 12.431835, -22.63725, 3.5784698, 12.431835, -22.63725, 3.5784698, 14.420786, -22.63725, 3.7813792, 12.431835, -22.728888, 3.786178, 20.38851, -22.63725, 3.7669787, 21.352875, -22.63725, 2.8034854, 20.838837, -22.63725, 3.321015, 20.838837, -22.63725, 3.321015, 21.352875, -22.63725, 2.8034854, 21.352875, -22.63725, 3.1997046, 21.352875, -22.63725, 3.1997046, 21.264729, -22.776888, 3.3083596, 20.838837, -22.63725, 3.321015, 21.264729, -22.776888, 3.3083596, 21.352875, -22.63725, 3.4733052, 20.838837, -22.63725, 3.321015, 20.38851, -22.63725, 3.7669787, 20.838837, -22.63725, 3.321015, 21.352875, -22.63725, 3.4733052, 20.38851, -22.63725, 3.7669787, 21.352875, -22.63725, 3.4733052, 21.352875, -22.63725, 3.786178, 20.38851, -23.00336, 2.7803578, 21.352875, -22.63725, 2.8034854, 20.38851, -22.63725, 2.7803578, 20.38851, -23.00336, 2.7803578, 21.352875, -23.00336, 2.8034854, 21.352875, -22.63725, 2.8034854, 12.431835, -22.63725, 2.9562135, 20.38851, -23.00336, 2.7803578, 20.38851, -22.63725, 2.7803578, 12.431835, -22.63725, 2.9562135, 12.431835, -23.00336, 2.9562135, 20.38851, -23.00336, 2.7803578, 21.352875, -23.00336, 3.786178, 20.38851, -23.00336, 2.7803578, 20.38851, -23.00336, 3.7669787, 20.38851, -23.00336, 2.7803578, 21.352875, -23.00336, 3.786178, 21.352875, -23.00336, 3.2546868, 20.38851, -23.00336, 2.7803578, 21.352875, -23.00336, 3.2546868, 21.352875, -23.00336, 2.8034854, 12.431835, -23.00336, 2.9562135, 20.38851, -23.00336, 3.7669787, 20.38851, -23.00336, 2.7803578, 12.431835, -23.00336, 2.9562135, 12.431835, -23.00336, 3.786178, 20.38851, -23.00336, 3.7669787, 21.352875, -22.63725, 3.4733052, 21.264729, -22.776888, 3.3083596, 21.352875, -23.00336, 3.2546868, 21.264729, -22.776888, 3.3083596, 21.352875, -22.63725, 3.1997046, 21.352875, -23.00336, 3.2546868, 21.352875, -23.00336, 3.786178, 21.352875, -22.63725, 3.4733052, 21.352875, -23.00336, 3.2546868, 21.352875, -23.00336, 3.786178, 21.352875, -22.63725, 3.786178, 21.352875, -22.63725, 3.4733052, 21.352875, -22.63725, 3.1997046, 21.352875, -23.00336, 2.8034854, 21.352875, -23.00336, 3.2546868, 21.352875, -22.63725, 3.1997046, 21.352875, -22.63725, 2.8034854, 21.352875, -23.00336, 2.8034854, 12.431835, -22.63725, 2.9562135, 12.431835, -23.00336, 3.786178, 12.431835, -23.00336, 2.9562135, 12.431835, -23.00336, 3.786178, 12.431835, -22.63725, 2.9562135, 12.431835, -22.63725, 3.5784698, 12.431835, -23.00336, 3.786178, 12.431835, -22.63725, 3.5784698, 12.431835, -22.728888, 3.786178, 14.157221, -23.00336, 0.4183159, 21.256002, -22.63725, 0.25860643, 14.157221, -22.63725, 0.4183159, 14.157221, -23.00336, 0.4183159, 21.256002, -23.00336, 0.25860643, 21.256002, -22.63725, 0.25860643, 12.528709, -22.63725, 0.41177034, 14.157221, -23.00336, 0.4183159, 14.157221, -22.63725, 0.4183159, 12.528709, -22.63725, 0.41177034, 12.528709, -23.00336, 0.41177034, 14.157221, -23.00336, 0.4183159, 14.157221, -23.00336, 1.4267545, 21.256002, -23.00336, 0.25860643, 14.157221, -23.00336, 0.4183159, 14.157221, -23.00336, 1.4267545, 21.256002, -23.00336, 1.2413001, 21.256002, -23.00336, 0.25860643, 12.528709, -23.00336, 0.41177034, 14.157221, -23.00336, 1.4267545, 14.157221, -23.00336, 0.4183159, 12.528709, -23.00336, 0.41177034, 12.528709, -23.00336, 1.2413001, 14.157221, -23.00336, 1.4267545, 12.528709, -23.00336, 0.41177034, 12.528709, -22.63725, 1.2413001, 12.528709, -23.00336, 1.2413001, 12.528709, -23.00336, 0.41177034, 12.528709, -22.63725, 0.41177034, 12.528709, -22.63725, 1.2413001, 14.157221, -22.63725, 1.1745362, 14.157221, -22.728888, 1.4267545, 13.750093, -22.63725, 1.3805003, 14.157221, -22.63725, 1.1745362, 15.931917, -22.63725, 1.3805003, 14.157221, -22.728888, 1.4267545, 14.157221, -22.63725, 1.1745362, 19.925089, -22.63725, 1.2762094, 15.931917, -22.63725, 1.3805003, 13.750093, -22.63725, 1.3805003, 14.157221, -22.63725, 0.4183159, 14.157221, -22.63725, 1.1745362, 13.750093, -22.63725, 1.3805003, 12.528709, -22.63725, 0.41177034, 14.157221, -22.63725, 0.4183159, 12.528709, -22.63725, 0.41177034, 13.750093, -22.63725, 1.3805003, 12.528709, -22.63725, 1.2413001, 21.256002, -22.63725, 0.25860643, 14.157221, -22.63725, 1.1745362, 14.157221, -22.63725, 0.4183159, 14.157221, -22.63725, 1.1745362, 21.256002, -22.63725, 0.25860643, 19.925089, -22.63725, 1.2762094, 19.925089, -22.63725, 1.2762094, 21.256002, -22.63725, 0.25860643, 21.256002, -22.63725, 0.99606276, 19.925089, -22.63725, 1.2762094, 21.256002, -22.63725, 0.99606276, 21.256002, -22.728888, 1.2413001, 12.528709, -22.63725, 1.2413001, 14.157221, -23.00336, 1.4267545, 12.528709, -23.00336, 1.2413001, 14.157221, -23.00336, 1.4267545, 12.528709, -22.63725, 1.2413001, 13.750093, -22.63725, 1.3805003, 14.157221, -23.00336, 1.4267545, 13.750093, -22.63725, 1.3805003, 14.157221, -22.728888, 1.4267545, 14.157221, -22.728888, 1.4267545, 21.256002, -23.00336, 1.2413001, 14.157221, -23.00336, 1.4267545, 14.157221, -22.728888, 1.4267545, 19.925089, -22.63725, 1.2762094, 21.256002, -23.00336, 1.2413001, 21.256002, -23.00336, 1.2413001, 19.925089, -22.63725, 1.2762094, 21.256002, -22.728888, 1.2413001, 19.925089, -22.63725, 1.2762094, 14.157221, -22.728888, 1.4267545, 15.931917, -22.63725, 1.3805003, 21.256002, -22.728888, 1.2413001, 21.256002, -23.00336, 0.25860643, 21.256002, -23.00336, 1.2413001, 21.256002, -23.00336, 0.25860643, 21.256002, -22.728888, 1.2413001, 21.256002, -22.63725, 0.99606276, 21.256002, -23.00336, 0.25860643, 21.256002, -22.63725, 0.99606276, 21.256002, -22.63725, 0.25860643, 17.195192, -22.439577, -16.726889, 21.299639, -22.073904, -16.912344, 17.195192, -22.073904, -16.726889, 17.195192, -22.439577, -16.726889, 21.299639, -22.439577, -16.912344, 21.299639, -22.073904, -16.912344, 12.960274, -22.073904, -16.912344, 17.195192, -22.439577, -16.726889, 17.195192, -22.073904, -16.726889, 12.960274, -22.073904, -16.912344, 12.960274, -22.439577, -16.912344, 17.195192, -22.439577, -16.726889, 17.195192, -22.439577, -15.748123, 21.299639, -22.439577, -16.912344, 17.195192, -22.439577, -16.726889, 17.195192, -22.439577, -15.748123, 21.299639, -22.439577, -16.082378, 21.299639, -22.439577, -16.912344, 17.195192, -22.439577, -16.726889, 12.960274, -22.439577, -15.779104, 17.195192, -22.439577, -15.748123, 17.195192, -22.439577, -16.726889, 12.960274, -22.439577, -16.355106, 12.960274, -22.439577, -15.779104, 12.960274, -22.439577, -16.355106, 17.195192, -22.439577, -16.726889, 12.960274, -22.439577, -16.912344, 21.299639, -22.439577, -16.082378, 21.299639, -22.073904, -16.912344, 21.299639, -22.439577, -16.912344, 21.299639, -22.439577, -16.082378, 21.299639, -22.073904, -16.082378, 21.299639, -22.073904, -16.912344, 12.960274, -22.073904, -16.422306, 13.119982, -22.182995, -16.32456, 12.960274, -22.439577, -16.355106, 17.195192, -22.073904, -15.992487, 17.195192, -22.165104, -15.748123, 16.136572, -22.073904, -15.755539, 17.195192, -22.073904, -15.992487, 18.221523, -22.073904, -15.831467, 17.195192, -22.165104, -15.748123, 17.195192, -22.073904, -15.992487, 21.299639, -22.073904, -16.082378, 18.221523, -22.073904, -15.831467, 17.195192, -22.073904, -15.992487, 21.299639, -22.073904, -16.912344, 21.299639, -22.073904, -16.082378, 21.299639, -22.073904, -16.912344, 17.195192, -22.073904, -15.992487, 17.195192, -22.073904, -16.726889, 16.136572, -22.073904, -15.755539, 17.195192, -22.073904, -16.726889, 17.195192, -22.073904, -15.992487, 17.195192, -22.073904, -16.726889, 12.960274, -22.073904, -16.422306, 12.960274, -22.073904, -16.912344, 17.195192, -22.073904, -16.726889, 13.688129, -22.073904, -16.321941, 12.960274, -22.073904, -16.422306, 16.136572, -22.073904, -15.755539, 13.688129, -22.073904, -16.321941, 17.195192, -22.073904, -16.726889, 13.119982, -22.182995, -16.32456, 12.960274, -22.073904, -16.422306, 13.688129, -22.073904, -16.321941, 12.960274, -22.073904, -16.14565, 13.119982, -22.182995, -16.32456, 13.688129, -22.073904, -16.321941, 13.688129, -22.073904, -16.321941, 16.136572, -22.073904, -15.755539, 12.960274, -22.073904, -16.14565, 12.960274, -22.073904, -16.14565, 16.136572, -22.073904, -15.755539, 12.960274, -22.073904, -15.779104, 12.960274, -22.439577, -16.912344, 12.960274, -22.073904, -16.422306, 12.960274, -22.439577, -16.355106, 12.960274, -22.439577, -16.912344, 12.960274, -22.073904, -16.912344, 12.960274, -22.073904, -16.422306, 12.960274, -22.073904, -16.14565, 12.960274, -22.439577, -15.779104, 12.960274, -22.439577, -16.355106, 12.960274, -22.073904, -16.14565, 12.960274, -22.073904, -15.779104, 12.960274, -22.439577, -15.779104, 13.119982, -22.182995, -16.32456, 12.960274, -22.073904, -16.14565, 12.960274, -22.439577, -16.355106, 12.960274, -22.073904, -15.779104, 17.195192, -22.439577, -15.748123, 12.960274, -22.439577, -15.779104, 17.195192, -22.439577, -15.748123, 12.960274, -22.073904, -15.779104, 16.136572, -22.073904, -15.755539, 17.195192, -22.439577, -15.748123, 16.136572, -22.073904, -15.755539, 17.195192, -22.165104, -15.748123, 17.195192, -22.165104, -15.748123, 21.299639, -22.439577, -16.082378, 17.195192, -22.439577, -15.748123, 17.195192, -22.165104, -15.748123, 21.299639, -22.073904, -16.082378, 21.299639, -22.439577, -16.082378, 21.299639, -22.073904, -16.082378, 17.195192, -22.165104, -15.748123, 18.221523, -22.073904, -15.831467, 20.39811, -22.073904, -13.344625, 21.299639, -22.439577, -13.325426, 20.39811, -22.439577, -13.344625, 20.39811, -22.073904, -13.344625, 21.299639, -22.073904, -13.325426, 21.299639, -22.439577, -13.325426, 20.39811, -22.439577, -13.344625, 14.819624, -22.073904, -13.330225, 20.39811, -22.073904, -13.344625, 20.39811, -22.439577, -13.344625, 12.960274, -22.165104, -13.325426, 14.819624, -22.073904, -13.330225, 12.960274, -22.165104, -13.325426, 20.39811, -22.439577, -13.344625, 12.960274, -22.439577, -13.325426, 21.299639, -22.073904, -14.308119, 20.39811, -22.073904, -13.344625, 20.39811, -22.073904, -14.331245, 14.819624, -22.073904, -13.330225, 20.39811, -22.073904, -14.331245, 20.39811, -22.073904, -13.344625, 14.819624, -22.073904, -13.330225, 12.960274, -22.073904, -14.154955, 20.39811, -22.073904, -14.331245, 12.960274, -22.073904, -14.154955, 14.819624, -22.073904, -13.330225, 12.960274, -22.073904, -13.5327, 12.960274, -22.073904, -13.5327, 14.819624, -22.073904, -13.330225, 12.960274, -22.165104, -13.325426, 20.39811, -22.073904, -13.344625, 21.299639, -22.073904, -14.308119, 20.818766, -22.073904, -13.7901535, 20.818766, -22.073904, -13.7901535, 21.299639, -22.073904, -14.308119, 21.299639, -22.073904, -13.9119, 21.299639, -22.073904, -13.9119, 21.217165, -22.213541, -13.803246, 20.818766, -22.073904, -13.7901535, 21.217165, -22.213541, -13.803246, 21.299639, -22.073904, -13.6383, 20.818766, -22.073904, -13.7901535, 20.39811, -22.073904, -13.344625, 20.818766, -22.073904, -13.7901535, 21.299639, -22.073904, -13.6383, 20.39811, -22.073904, -13.344625, 21.299639, -22.073904, -13.6383, 21.299639, -22.073904, -13.325426, 20.39811, -22.439577, -14.331245, 21.299639, -22.073904, -14.308119, 20.39811, -22.073904, -14.331245, 20.39811, -22.439577, -14.331245, 21.299639, -22.439577, -14.308119, 21.299639, -22.073904, -14.308119, 12.960274, -22.073904, -14.154955, 20.39811, -22.439577, -14.331245, 20.39811, -22.073904, -14.331245, 12.960274, -22.073904, -14.154955, 12.960274, -22.439577, -14.154955, 20.39811, -22.439577, -14.331245, 21.299639, -22.439577, -13.325426, 20.39811, -22.439577, -14.331245, 20.39811, -22.439577, -13.344625, 20.39811, -22.439577, -14.331245, 21.299639, -22.439577, -13.325426, 21.299639, -22.439577, -13.856482, 20.39811, -22.439577, -14.331245, 21.299639, -22.439577, -13.856482, 21.299639, -22.439577, -14.308119, 12.960274, -22.439577, -14.154955, 20.39811, -22.439577, -13.344625, 20.39811, -22.439577, -14.331245, 12.960274, -22.439577, -14.154955, 12.960274, -22.439577, -13.325426, 20.39811, -22.439577, -13.344625, 21.299639, -22.073904, -13.6383, 21.217165, -22.213541, -13.803246, 21.299639, -22.439577, -13.856482, 21.217165, -22.213541, -13.803246, 21.299639, -22.073904, -13.9119, 21.299639, -22.439577, -13.856482, 21.299639, -22.439577, -13.325426, 21.299639, -22.073904, -13.6383, 21.299639, -22.439577, -13.856482, 21.299639, -22.439577, -13.325426, 21.299639, -22.073904, -13.325426, 21.299639, -22.073904, -13.6383, 21.299639, -22.073904, -13.9119, 21.299639, -22.439577, -14.308119, 21.299639, -22.439577, -13.856482, 21.299639, -22.073904, -13.9119, 21.299639, -22.073904, -14.308119, 21.299639, -22.439577, -14.308119, 12.960274, -22.073904, -14.154955, 12.960274, -22.439577, -13.325426, 12.960274, -22.439577, -14.154955, 12.960274, -22.439577, -13.325426, 12.960274, -22.073904, -14.154955, 12.960274, -22.073904, -13.5327, 12.960274, -22.439577, -13.325426, 12.960274, -22.073904, -13.5327, 12.960274, -22.165104, -13.325426, 13.295837, -22.635942, 4.8980365, 13.527548, -22.543432, 4.8980365, 13.527548, -22.635942, 4.8980365, 13.295837, -22.635942, 4.8980365, 13.295837, -22.543432, 4.8980365, 13.527548, -22.543432, 4.8980365, 13.295837, -22.543432, 4.8980365, 13.527548, -22.543432, -19.742168, 13.527548, -22.543432, 4.8980365, 13.295837, -22.543432, 4.8980365, 13.295837, -22.543432, -19.742168, 13.527548, -22.543432, -19.742168, 13.295837, -22.543432, -19.742168, 13.527548, -22.635942, -19.742168, 13.527548, -22.543432, -19.742168, 13.295837, -22.543432, -19.742168, 13.295837, -22.635942, -19.742168, 13.527548, -22.635942, -19.742168, 13.295837, -22.635942, -19.742168, 13.527548, -22.635942, 4.8980365, 13.527548, -22.635942, -19.742168, 13.295837, -22.635942, -19.742168, 13.295837, -22.635942, 4.8980365, 13.527548, -22.635942, 4.8980365, 13.527548, -22.635942, 4.8980365, 13.527548, -22.543432, -19.742168, 13.527548, -22.635942, -19.742168, 13.527548, -22.635942, 4.8980365, 13.527548, -22.543432, 4.8980365, 13.527548, -22.543432, -19.742168, 13.295837, -22.635942, -19.742168, 13.295837, -22.543432, 4.8980365, 13.295837, -22.635942, 4.8980365, 13.295837, -22.635942, -19.742168, 13.295837, -22.543432, -19.742168, 13.295837, -22.543432, 4.8980365, 14.33395, -22.439577, -18.07831, 21.590258, -22.073904, -18.237583, 14.33395, -22.073904, -18.07831, 14.33395, -22.439577, -18.07831, 21.590258, -22.439577, -18.237583, 21.590258, -22.073904, -18.237583, 12.669218, -22.073904, -18.084854, 14.33395, -22.439577, -18.07831, 14.33395, -22.073904, -18.07831, 12.669218, -22.073904, -18.084854, 12.669218, -22.439577, -18.084854, 14.33395, -22.439577, -18.07831, 14.33395, -22.439577, -17.069435, 21.590258, -22.439577, -18.237583, 14.33395, -22.439577, -18.07831, 14.33395, -22.439577, -17.069435, 21.590258, -22.439577, -17.25489, 21.590258, -22.439577, -18.237583, 12.669218, -22.439577, -18.084854, 14.33395, -22.439577, -17.069435, 14.33395, -22.439577, -18.07831, 12.669218, -22.439577, -18.084854, 12.669218, -22.439577, -17.25489, 14.33395, -22.439577, -17.069435, 12.669218, -22.439577, -18.084854, 12.669218, -22.073904, -17.25489, 12.669218, -22.439577, -17.25489, 12.669218, -22.439577, -18.084854, 12.669218, -22.073904, -18.084854, 12.669218, -22.073904, -17.25489, 14.33395, -22.073904, -17.321653, 14.33395, -22.165104, -17.069435, 13.917658, -22.073904, -17.115688, 14.33395, -22.073904, -17.321653, 16.147917, -22.073904, -17.115688, 14.33395, -22.165104, -17.069435, 14.33395, -22.073904, -17.321653, 20.229671, -22.073904, -17.220417, 16.147917, -22.073904, -17.115688, 13.917658, -22.073904, -17.115688, 14.33395, -22.073904, -18.07831, 14.33395, -22.073904, -17.321653, 13.917658, -22.073904, -17.115688, 12.669218, -22.073904, -18.084854, 14.33395, -22.073904, -18.07831, 12.669218, -22.073904, -18.084854, 13.917658, -22.073904, -17.115688, 12.669218, -22.073904, -17.25489, 21.590258, -22.073904, -18.237583, 14.33395, -22.073904, -17.321653, 14.33395, -22.073904, -18.07831, 14.33395, -22.073904, -17.321653, 21.590258, -22.073904, -18.237583, 20.229671, -22.073904, -17.220417, 20.229671, -22.073904, -17.220417, 21.590258, -22.073904, -18.237583, 21.590258, -22.073904, -17.501, 20.229671, -22.073904, -17.220417, 21.590258, -22.073904, -17.501, 21.590258, -22.165104, -17.25489, 12.669218, -22.073904, -17.25489, 14.33395, -22.439577, -17.069435, 12.669218, -22.439577, -17.25489, 14.33395, -22.439577, -17.069435, 12.669218, -22.073904, -17.25489, 13.917658, -22.073904, -17.115688, 14.33395, -22.439577, -17.069435, 13.917658, -22.073904, -17.115688, 14.33395, -22.165104, -17.069435, 14.33395, -22.165104, -17.069435, 21.590258, -22.439577, -17.25489, 14.33395, -22.439577, -17.069435, 14.33395, -22.165104, -17.069435, 20.229671, -22.073904, -17.220417, 21.590258, -22.439577, -17.25489, 21.590258, -22.439577, -17.25489, 20.229671, -22.073904, -17.220417, 21.590258, -22.165104, -17.25489, 20.229671, -22.073904, -17.220417, 14.33395, -22.165104, -17.069435, 16.147917, -22.073904, -17.115688, 21.590258, -22.165104, -17.25489, 21.590258, -22.439577, -18.237583, 21.590258, -22.439577, -17.25489, 21.590258, -22.439577, -18.237583, 21.590258, -22.165104, -17.25489, 21.590258, -22.073904, -17.501, 21.590258, -22.439577, -18.237583, 21.590258, -22.073904, -17.501, 21.590258, -22.073904, -18.237583, 12.749508, -25.90344, -19.752205, 13.527111, -21.174994, -19.579403, 13.354311, -25.90344, -19.752205, 12.749508, -25.90344, -19.752205, 12.57671, -21.174994, -19.579403, 13.527111, -21.174994, -19.579403, 12.57671, -21.174994, -19.579403, 13.527111, -21.174994, -20.52937, 13.527111, -21.174994, -19.579403, 12.57671, -21.174994, -19.579403, 12.57671, -21.174994, -20.52937, 13.527111, -21.174994, -20.52937, 12.57671, -21.174994, -20.52937, 13.354311, -25.90344, -20.356571, 13.527111, -21.174994, -20.52937, 12.57671, -21.174994, -20.52937, 12.749508, -25.90344, -20.356571, 13.354311, -25.90344, -20.356571, 12.749508, -25.90344, -20.356571, 13.354311, -25.90344, -19.752205, 13.354311, -25.90344, -20.356571, 12.749508, -25.90344, -20.356571, 12.749508, -25.90344, -19.752205, 13.354311, -25.90344, -19.752205, 13.354311, -25.90344, -19.752205, 13.527111, -21.174994, -20.52937, 13.354311, -25.90344, -20.356571, 13.354311, -25.90344, -19.752205, 13.527111, -21.174994, -19.579403, 13.527111, -21.174994, -20.52937, 12.749508, -25.90344, -20.356571, 12.57671, -21.174994, -19.579403, 12.749508, -25.90344, -19.752205, 12.749508, -25.90344, -20.356571, 12.57671, -21.174994, -20.52937, 12.57671, -21.174994, -19.579403, 20.39811, -22.073904, -18.576202, 21.299639, -22.439577, -18.556564, 20.39811, -22.439577, -18.576202, 20.39811, -22.073904, -18.576202, 21.299639, -22.073904, -18.556564, 21.299639, -22.439577, -18.556564, 20.39811, -22.439577, -18.576202, 14.819624, -22.073904, -18.561365, 20.39811, -22.073904, -18.576202, 20.39811, -22.439577, -18.576202, 12.960274, -22.165104, -18.556564, 14.819624, -22.073904, -18.561365, 12.960274, -22.165104, -18.556564, 20.39811, -22.439577, -18.576202, 12.960274, -22.439577, -18.556564, 21.299639, -22.073904, -19.539696, 20.39811, -22.073904, -18.576202, 20.39811, -22.073904, -19.562386, 14.819624, -22.073904, -18.561365, 20.39811, -22.073904, -19.562386, 20.39811, -22.073904, -18.576202, 14.819624, -22.073904, -18.561365, 12.960274, -22.073904, -19.386532, 20.39811, -22.073904, -19.562386, 12.960274, -22.073904, -19.386532, 14.819624, -22.073904, -18.561365, 12.960274, -22.073904, -18.764275, 12.960274, -22.073904, -18.764275, 14.819624, -22.073904, -18.561365, 12.960274, -22.165104, -18.556564, 20.39811, -22.073904, -18.576202, 21.299639, -22.073904, -19.539696, 20.818766, -22.073904, -19.022167, 20.818766, -22.073904, -19.022167, 21.299639, -22.073904, -19.539696, 21.299639, -22.073904, -19.143913, 21.299639, -22.073904, -19.143913, 21.217165, -22.213541, -19.034386, 20.818766, -22.073904, -19.022167, 21.217165, -22.213541, -19.034386, 21.299639, -22.073904, -18.869438, 20.818766, -22.073904, -19.022167, 20.39811, -22.073904, -18.576202, 20.818766, -22.073904, -19.022167, 21.299639, -22.073904, -18.869438, 20.39811, -22.073904, -18.576202, 21.299639, -22.073904, -18.869438, 21.299639, -22.073904, -18.556564, 20.39811, -22.439577, -19.562386, 21.299639, -22.073904, -19.539696, 20.39811, -22.073904, -19.562386, 20.39811, -22.439577, -19.562386, 21.299639, -22.439577, -19.539696, 21.299639, -22.073904, -19.539696, 12.960274, -22.073904, -19.386532, 20.39811, -22.439577, -19.562386, 20.39811, -22.073904, -19.562386, 12.960274, -22.073904, -19.386532, 12.960274, -22.439577, -19.386532, 20.39811, -22.439577, -19.562386, 21.299639, -22.439577, -18.556564, 20.39811, -22.439577, -19.562386, 20.39811, -22.439577, -18.576202, 20.39811, -22.439577, -19.562386, 21.299639, -22.439577, -18.556564, 21.299639, -22.439577, -19.088493, 20.39811, -22.439577, -19.562386, 21.299639, -22.439577, -19.088493, 21.299639, -22.439577, -19.539696, 12.960274, -22.439577, -19.386532, 20.39811, -22.439577, -18.576202, 20.39811, -22.439577, -19.562386, 12.960274, -22.439577, -19.386532, 12.960274, -22.439577, -18.556564, 20.39811, -22.439577, -18.576202, 21.299639, -22.073904, -18.869438, 21.217165, -22.213541, -19.034386, 21.299639, -22.439577, -19.088493, 21.217165, -22.213541, -19.034386, 21.299639, -22.073904, -19.143913, 21.299639, -22.439577, -19.088493, 21.299639, -22.439577, -18.556564, 21.299639, -22.073904, -18.869438, 21.299639, -22.439577, -19.088493, 21.299639, -22.439577, -18.556564, 21.299639, -22.073904, -18.556564, 21.299639, -22.073904, -18.869438, 21.299639, -22.073904, -19.143913, 21.299639, -22.439577, -19.539696, 21.299639, -22.439577, -19.088493, 21.299639, -22.073904, -19.143913, 21.299639, -22.073904, -19.539696, 21.299639, -22.439577, -19.539696, 12.960274, -22.073904, -19.386532, 12.960274, -22.439577, -18.556564, 12.960274, -22.439577, -19.386532, 12.960274, -22.439577, -18.556564, 12.960274, -22.073904, -19.386532, 12.960274, -22.073904, -18.764275, 12.960274, -22.439577, -18.556564, 12.960274, -22.073904, -18.764275, 12.960274, -22.165104, -18.556564, 20.62589, -22.073904, -12.164696, 21.590258, -22.439577, -12.14506, 20.62589, -22.439577, -12.164696, 20.62589, -22.073904, -12.164696, 21.590258, -22.073904, -12.14506, 21.590258, -22.439577, -12.14506, 20.62589, -22.439577, -12.164696, 14.658169, -22.073904, -12.149858, 20.62589, -22.073904, -12.164696, 20.62589, -22.439577, -12.164696, 12.669218, -22.165104, -12.14506, 14.658169, -22.073904, -12.149858, 12.669218, -22.165104, -12.14506, 20.62589, -22.439577, -12.164696, 12.669218, -22.439577, -12.14506, 21.590258, -22.073904, -13.127752, 20.62589, -22.073904, -12.164696, 20.62589, -22.073904, -13.150879, 14.658169, -22.073904, -12.149858, 20.62589, -22.073904, -13.150879, 20.62589, -22.073904, -12.164696, 14.658169, -22.073904, -12.149858, 12.669218, -22.073904, -12.975025, 20.62589, -22.073904, -13.150879, 12.669218, -22.073904, -12.975025, 14.658169, -22.073904, -12.149858, 12.669218, -22.073904, -12.352768, 12.669218, -22.073904, -12.352768, 14.658169, -22.073904, -12.149858, 12.669218, -22.165104, -12.14506, 20.62589, -22.073904, -12.164696, 21.590258, -22.073904, -13.127752, 21.07622, -22.073904, -12.610224, 21.07622, -22.073904, -12.610224, 21.590258, -22.073904, -13.127752, 21.590258, -22.073904, -12.73197, 21.590258, -22.073904, -12.73197, 21.502111, -22.213541, -12.622879, 21.07622, -22.073904, -12.610224, 21.502111, -22.213541, -12.622879, 21.590258, -22.073904, -12.457933, 21.07622, -22.073904, -12.610224, 20.62589, -22.073904, -12.164696, 21.07622, -22.073904, -12.610224, 21.590258, -22.073904, -12.457933, 20.62589, -22.073904, -12.164696, 21.590258, -22.073904, -12.457933, 21.590258, -22.073904, -12.14506, 20.62589, -22.439577, -13.150879, 21.590258, -22.073904, -13.127752, 20.62589, -22.073904, -13.150879, 20.62589, -22.439577, -13.150879, 21.590258, -22.439577, -13.127752, 21.590258, -22.073904, -13.127752, 12.669218, -22.073904, -12.975025, 20.62589, -22.439577, -13.150879, 20.62589, -22.073904, -13.150879, 12.669218, -22.073904, -12.975025, 12.669218, -22.439577, -12.975025, 20.62589, -22.439577, -13.150879, 21.590258, -22.439577, -12.14506, 20.62589, -22.439577, -13.150879, 20.62589, -22.439577, -12.164696, 20.62589, -22.439577, -13.150879, 21.590258, -22.439577, -12.14506, 21.590258, -22.439577, -12.676552, 20.62589, -22.439577, -13.150879, 21.590258, -22.439577, -12.676552, 21.590258, -22.439577, -13.127752, 12.669218, -22.439577, -12.975025, 20.62589, -22.439577, -12.164696, 20.62589, -22.439577, -13.150879, 12.669218, -22.439577, -12.975025, 12.669218, -22.439577, -12.14506, 20.62589, -22.439577, -12.164696, 21.590258, -22.073904, -12.457933, 21.502111, -22.213541, -12.622879, 21.590258, -22.439577, -12.676552, 21.502111, -22.213541, -12.622879, 21.590258, -22.073904, -12.73197, 21.590258, -22.439577, -12.676552, 21.590258, -22.439577, -12.14506, 21.590258, -22.073904, -12.457933, 21.590258, -22.439577, -12.676552, 21.590258, -22.439577, -12.14506, 21.590258, -22.073904, -12.14506, 21.590258, -22.073904, -12.457933, 21.590258, -22.073904, -12.73197, 21.590258, -22.439577, -13.127752, 21.590258, -22.439577, -12.676552, 21.590258, -22.073904, -12.73197, 21.590258, -22.073904, -13.127752, 21.590258, -22.439577, -13.127752, 12.669218, -22.073904, -12.975025, 12.669218, -22.439577, -12.14506, 12.669218, -22.439577, -12.975025, 12.669218, -22.439577, -12.14506, 12.669218, -22.073904, -12.975025, 12.669218, -22.073904, -12.352768, 12.669218, -22.439577, -12.14506, 12.669218, -22.073904, -12.352768, 12.669218, -22.165104, -12.14506, 17.199993, -22.439577, -11.627094, 21.590258, -22.073904, -11.812113, 17.199993, -22.073904, -11.627094, 17.199993, -22.439577, -11.627094, 21.590258, -22.439577, -11.812113, 21.590258, -22.073904, -11.812113, 12.669218, -22.073904, -11.812113, 17.199993, -22.439577, -11.627094, 17.199993, -22.073904, -11.627094, 12.669218, -22.073904, -11.812113, 12.669218, -22.439577, -11.812113, 17.199993, -22.439577, -11.627094, 17.199993, -22.439577, -10.647455, 21.590258, -22.439577, -11.812113, 17.199993, -22.439577, -11.627094, 17.199993, -22.439577, -10.647455, 21.590258, -22.439577, -10.982147, 21.590258, -22.439577, -11.812113, 17.199993, -22.439577, -11.627094, 12.669218, -22.439577, -10.678438, 17.199993, -22.439577, -10.647455, 17.199993, -22.439577, -11.627094, 12.669218, -22.439577, -11.254438, 12.669218, -22.439577, -10.678438, 12.669218, -22.439577, -11.254438, 17.199993, -22.439577, -11.627094, 12.669218, -22.439577, -11.812113, 21.590258, -22.439577, -10.982147, 21.590258, -22.073904, -11.812113, 21.590258, -22.439577, -11.812113, 21.590258, -22.439577, -10.982147, 21.590258, -22.073904, -10.982147, 21.590258, -22.073904, -11.812113, 12.669218, -22.073904, -11.32164, 12.840273, -22.182995, -11.224329, 12.669218, -22.439577, -11.254438, 17.199993, -22.073904, -10.892256, 17.199993, -22.165104, -10.647455, 16.06719, -22.073904, -10.65531, 17.199993, -22.073904, -10.892256, 18.29745, -22.073904, -10.731237, 17.199993, -22.165104, -10.647455, 17.199993, -22.073904, -10.892256, 21.590258, -22.073904, -10.982147, 18.29745, -22.073904, -10.731237, 17.199993, -22.073904, -10.892256, 21.590258, -22.073904, -11.812113, 21.590258, -22.073904, -10.982147, 21.590258, -22.073904, -11.812113, 17.199993, -22.073904, -10.892256, 17.199993, -22.073904, -11.627094, 16.06719, -22.073904, -10.65531, 17.199993, -22.073904, -11.627094, 17.199993, -22.073904, -10.892256, 17.199993, -22.073904, -11.627094, 12.669218, -22.073904, -11.32164, 12.669218, -22.073904, -11.812113, 17.199993, -22.073904, -11.627094, 13.447693, -22.073904, -11.221275, 12.669218, -22.073904, -11.32164, 16.06719, -22.073904, -10.65531, 13.447693, -22.073904, -11.221275, 17.199993, -22.073904, -11.627094, 12.840273, -22.182995, -11.224329, 12.669218, -22.073904, -11.32164, 13.447693, -22.073904, -11.221275, 12.669218, -22.073904, -11.044985, 12.840273, -22.182995, -11.224329, 13.447693, -22.073904, -11.221275, 13.447693, -22.073904, -11.221275, 16.06719, -22.073904, -10.65531, 12.669218, -22.073904, -11.044985, 12.669218, -22.073904, -11.044985, 16.06719, -22.073904, -10.65531, 12.669218, -22.073904, -10.678438, 12.669218, -22.439577, -11.812113, 12.669218, -22.073904, -11.32164, 12.669218, -22.439577, -11.254438, 12.669218, -22.439577, -11.812113, 12.669218, -22.073904, -11.812113, 12.669218, -22.073904, -11.32164, 12.669218, -22.073904, -11.044985, 12.669218, -22.439577, -10.678438, 12.669218, -22.439577, -11.254438, 12.669218, -22.073904, -11.044985, 12.669218, -22.073904, -10.678438, 12.669218, -22.439577, -10.678438, 12.840273, -22.182995, -11.224329, 12.669218, -22.073904, -11.044985, 12.669218, -22.439577, -11.254438, 12.669218, -22.073904, -10.678438, 17.199993, -22.439577, -10.647455, 12.669218, -22.439577, -10.678438, 17.199993, -22.439577, -10.647455, 12.669218, -22.073904, -10.678438, 16.06719, -22.073904, -10.65531, 17.199993, -22.439577, -10.647455, 16.06719, -22.073904, -10.65531, 17.199993, -22.165104, -10.647455, 17.199993, -22.165104, -10.647455, 21.590258, -22.439577, -10.982147, 17.199993, -22.439577, -10.647455, 17.199993, -22.165104, -10.647455, 21.590258, -22.073904, -10.982147, 21.590258, -22.439577, -10.982147, 21.590258, -22.073904, -10.982147, 17.199993, -22.165104, -10.647455, 18.29745, -22.073904, -10.731237, 20.39811, -22.073904, -9.367163, 21.299639, -22.439577, -9.347525, 20.39811, -22.439577, -9.367163, 20.39811, -22.073904, -9.367163, 21.299639, -22.073904, -9.347525, 21.299639, -22.439577, -9.347525, 20.39811, -22.439577, -9.367163, 14.819624, -22.073904, -9.352325, 20.39811, -22.073904, -9.367163, 20.39811, -22.439577, -9.367163, 12.960274, -22.165104, -9.347525, 14.819624, -22.073904, -9.352325, 12.960274, -22.165104, -9.347525, 20.39811, -22.439577, -9.367163, 12.960274, -22.439577, -9.347525, 21.299639, -22.073904, -10.330656, 20.39811, -22.073904, -9.367163, 20.39811, -22.073904, -10.353346, 14.819624, -22.073904, -9.352325, 20.39811, -22.073904, -10.353346, 20.39811, -22.073904, -9.367163, 14.819624, -22.073904, -9.352325, 12.960274, -22.073904, -10.17749, 20.39811, -22.073904, -10.353346, 12.960274, -22.073904, -10.17749, 14.819624, -22.073904, -9.352325, 12.960274, -22.073904, -9.555235, 12.960274, -22.073904, -9.555235, 14.819624, -22.073904, -9.352325, 12.960274, -22.165104, -9.347525, 20.39811, -22.073904, -9.367163, 21.299639, -22.073904, -10.330656, 20.818766, -22.073904, -9.8131275, 20.818766, -22.073904, -9.8131275, 21.299639, -22.073904, -10.330656, 21.299639, -22.073904, -9.934872, 21.299639, -22.073904, -9.934872, 21.217165, -22.213541, -9.825344, 20.818766, -22.073904, -9.8131275, 21.217165, -22.213541, -9.825344, 21.299639, -22.073904, -9.6603985, 20.818766, -22.073904, -9.8131275, 20.39811, -22.073904, -9.367163, 20.818766, -22.073904, -9.8131275, 21.299639, -22.073904, -9.6603985, 20.39811, -22.073904, -9.367163, 21.299639, -22.073904, -9.6603985, 21.299639, -22.073904, -9.347525, 20.39811, -22.439577, -10.353346, 21.299639, -22.073904, -10.330656, 20.39811, -22.073904, -10.353346, 20.39811, -22.439577, -10.353346, 21.299639, -22.439577, -10.330656, 21.299639, -22.073904, -10.330656, 12.960274, -22.073904, -10.17749, 20.39811, -22.439577, -10.353346, 20.39811, -22.073904, -10.353346, 12.960274, -22.073904, -10.17749, 12.960274, -22.439577, -10.17749, 20.39811, -22.439577, -10.353346, 21.299639, -22.439577, -9.347525, 20.39811, -22.439577, -10.353346, 20.39811, -22.439577, -9.367163, 20.39811, -22.439577, -10.353346, 21.299639, -22.439577, -9.347525, 21.299639, -22.439577, -9.879017, 20.39811, -22.439577, -10.353346, 21.299639, -22.439577, -9.879017, 21.299639, -22.439577, -10.330656, 12.960274, -22.439577, -10.17749, 20.39811, -22.439577, -9.367163, 20.39811, -22.439577, -10.353346, 12.960274, -22.439577, -10.17749, 12.960274, -22.439577, -9.347525, 20.39811, -22.439577, -9.367163, 21.299639, -22.073904, -9.6603985, 21.217165, -22.213541, -9.825344, 21.299639, -22.439577, -9.879017, 21.217165, -22.213541, -9.825344, 21.299639, -22.073904, -9.934872, 21.299639, -22.439577, -9.879017, 21.299639, -22.439577, -9.347525, 21.299639, -22.073904, -9.6603985, 21.299639, -22.439577, -9.879017, 21.299639, -22.439577, -9.347525, 21.299639, -22.073904, -9.347525, 21.299639, -22.073904, -9.6603985, 21.299639, -22.073904, -9.934872, 21.299639, -22.439577, -10.330656, 21.299639, -22.439577, -9.879017, 21.299639, -22.073904, -9.934872, 21.299639, -22.073904, -10.330656, 21.299639, -22.439577, -10.330656, 12.960274, -22.073904, -10.17749, 12.960274, -22.439577, -9.347525, 12.960274, -22.439577, -10.17749, 12.960274, -22.439577, -9.347525, 12.960274, -22.073904, -10.17749, 12.960274, -22.073904, -9.555235, 12.960274, -22.439577, -9.347525, 12.960274, -22.073904, -9.555235, 12.960274, -22.165104, -9.347525, 20.474909, -25.90344, -19.689804, 21.125528, -21.174994, -19.768787, 21.204512, -25.90344, -19.689804, 20.474909, -25.90344, -19.689804, 20.553892, -21.174994, -19.768787, 21.125528, -21.174994, -19.768787, 20.553892, -21.174994, -19.768787, 21.125528, -21.174994, -20.340424, 21.125528, -21.174994, -19.768787, 20.553892, -21.174994, -19.768787, 20.553892, -21.174994, -20.340424, 21.125528, -21.174994, -20.340424, 20.553892, -21.174994, -20.340424, 21.204512, -25.90344, -20.419407, 21.125528, -21.174994, -20.340424, 20.553892, -21.174994, -20.340424, 20.474909, -25.90344, -20.419407, 21.204512, -25.90344, -20.419407, 20.474909, -25.90344, -20.419407, 21.204512, -25.90344, -19.689804, 21.204512, -25.90344, -20.419407, 20.474909, -25.90344, -20.419407, 20.474909, -25.90344, -19.689804, 21.204512, -25.90344, -19.689804, 21.204512, -25.90344, -19.689804, 21.125528, -21.174994, -20.340424, 21.204512, -25.90344, -20.419407, 21.204512, -25.90344, -19.689804, 21.125528, -21.174994, -19.768787, 21.125528, -21.174994, -20.340424, 20.474909, -25.90344, -20.419407, 20.553892, -21.174994, -19.768787, 20.474909, -25.90344, -19.689804, 20.474909, -25.90344, -20.419407, 20.553892, -21.174994, -20.340424, 20.553892, -21.174994, -19.768787, 12.886963, -25.90344, 5.2994914, 13.6170025, -21.174994, 5.2994914, 13.6170025, -25.90344, 5.2994914, 12.886963, -25.90344, 5.2994914, 12.886963, -21.174994, 5.2994914, 13.6170025, -21.174994, 5.2994914, 12.886963, -25.90344, 4.5698895, 13.6170025, -25.90344, 5.2994914, 13.6170025, -25.90344, 4.5698895, 12.886963, -25.90344, 4.5698895, 12.886963, -25.90344, 5.2994914, 13.6170025, -25.90344, 5.2994914, 13.6170025, -25.90344, 5.2994914, 13.6170025, -21.174994, 4.5698895, 13.6170025, -25.90344, 4.5698895, 13.6170025, -25.90344, 5.2994914, 13.6170025, -21.174994, 5.2994914, 13.6170025, -21.174994, 4.5698895, 12.886963, -22.357105, 4.5698895, 13.069365, -21.174994, 4.5698895, 12.886963, -21.174994, 4.7522902, 12.886963, -25.90344, 5.2994914, 12.886963, -22.357105, 4.5698895, 12.886963, -21.174994, 4.7522902, 12.886963, -22.357105, 4.5698895, 12.886963, -25.90344, 5.2994914, 12.886963, -25.90344, 4.5698895, 12.886963, -25.90344, 5.2994914, 12.886963, -21.174994, 4.7522902, 12.886963, -21.174994, 5.2994914, 12.886963, -21.174994, 4.7522902, 13.6170025, -21.174994, 5.2994914, 12.886963, -21.174994, 5.2994914, 13.6170025, -21.174994, 5.2994914, 12.886963, -21.174994, 4.7522902, 13.069365, -21.174994, 4.5698895, 13.6170025, -21.174994, 5.2994914, 13.069365, -21.174994, 4.5698895, 13.6170025, -21.174994, 4.5698895, 12.886963, -25.90344, 4.5698895, 13.069365, -21.174994, 4.5698895, 12.886963, -22.357105, 4.5698895, 13.069365, -21.174994, 4.5698895, 12.886963, -25.90344, 4.5698895, 13.6170025, -25.90344, 4.5698895, 13.069365, -21.174994, 4.5698895, 13.6170025, -25.90344, 4.5698895, 13.6170025, -21.174994, 4.5698895, 17.195192, -22.439577, -7.5178485, 21.299639, -22.073904, -7.703304, 17.195192, -22.073904, -7.5178485, 17.195192, -22.439577, -7.5178485, 21.299639, -22.439577, -7.703304, 21.299639, -22.073904, -7.703304, 12.960274, -22.073904, -7.703304, 17.195192, -22.439577, -7.5178485, 17.195192, -22.073904, -7.5178485, 12.960274, -22.073904, -7.703304, 12.960274, -22.439577, -7.703304, 17.195192, -22.439577, -7.5178485, 17.195192, -22.439577, -6.539082, 21.299639, -22.439577, -7.703304, 17.195192, -22.439577, -7.5178485, 17.195192, -22.439577, -6.539082, 21.299639, -22.439577, -6.873338, 21.299639, -22.439577, -7.703304, 17.195192, -22.439577, -7.5178485, 12.960274, -22.439577, -6.570064, 17.195192, -22.439577, -6.539082, 17.195192, -22.439577, -7.5178485, 12.960274, -22.439577, -7.146065, 12.960274, -22.439577, -6.570064, 12.960274, -22.439577, -7.146065, 17.195192, -22.439577, -7.5178485, 12.960274, -22.439577, -7.703304, 21.299639, -22.439577, -6.873338, 21.299639, -22.073904, -7.703304, 21.299639, -22.439577, -7.703304, 21.299639, -22.439577, -6.873338, 21.299639, -22.073904, -6.873338, 21.299639, -22.073904, -7.703304, 12.960274, -22.073904, -7.2123933, 13.119982, -22.182995, -7.11552, 12.960274, -22.439577, -7.146065, 17.195192, -22.073904, -6.783447, 17.195192, -22.165104, -6.539082, 16.136572, -22.073904, -6.5465007, 17.195192, -22.073904, -6.783447, 18.221523, -22.073904, -6.6224284, 17.195192, -22.165104, -6.539082, 17.195192, -22.073904, -6.783447, 21.299639, -22.073904, -6.873338, 18.221523, -22.073904, -6.6224284, 17.195192, -22.073904, -6.783447, 21.299639, -22.073904, -7.703304, 21.299639, -22.073904, -6.873338, 21.299639, -22.073904, -7.703304, 17.195192, -22.073904, -6.783447, 17.195192, -22.073904, -7.5178485, 16.136572, -22.073904, -6.5465007, 17.195192, -22.073904, -7.5178485, 17.195192, -22.073904, -6.783447, 17.195192, -22.073904, -7.5178485, 12.960274, -22.073904, -7.2123933, 12.960274, -22.073904, -7.703304, 17.195192, -22.073904, -7.5178485, 13.688129, -22.073904, -7.112902, 12.960274, -22.073904, -7.2123933, 16.136572, -22.073904, -6.5465007, 13.688129, -22.073904, -7.112902, 17.195192, -22.073904, -7.5178485, 13.119982, -22.182995, -7.11552, 12.960274, -22.073904, -7.2123933, 13.688129, -22.073904, -7.112902, 12.960274, -22.073904, -6.9366107, 13.119982, -22.182995, -7.11552, 13.688129, -22.073904, -7.112902, 13.688129, -22.073904, -7.112902, 16.136572, -22.073904, -6.5465007, 12.960274, -22.073904, -6.9366107, 12.960274, -22.073904, -6.9366107, 16.136572, -22.073904, -6.5465007, 12.960274, -22.073904, -6.570064, 12.960274, -22.439577, -7.703304, 12.960274, -22.073904, -7.2123933, 12.960274, -22.439577, -7.146065, 12.960274, -22.439577, -7.703304, 12.960274, -22.073904, -7.703304, 12.960274, -22.073904, -7.2123933, 12.960274, -22.073904, -6.9366107, 12.960274, -22.439577, -6.570064, 12.960274, -22.439577, -7.146065, 12.960274, -22.073904, -6.9366107, 12.960274, -22.073904, -6.570064, 12.960274, -22.439577, -6.570064, 13.119982, -22.182995, -7.11552, 12.960274, -22.073904, -6.9366107, 12.960274, -22.439577, -7.146065, 12.960274, -22.073904, -6.570064, 17.195192, -22.439577, -6.539082, 12.960274, -22.439577, -6.570064, 17.195192, -22.439577, -6.539082, 12.960274, -22.073904, -6.570064, 16.136572, -22.073904, -6.5465007, 17.195192, -22.439577, -6.539082, 16.136572, -22.073904, -6.5465007, 17.195192, -22.165104, -6.539082, 17.195192, -22.165104, -6.539082, 21.299639, -22.439577, -6.873338, 17.195192, -22.439577, -6.539082, 17.195192, -22.165104, -6.539082, 21.299639, -22.073904, -6.873338, 21.299639, -22.439577, -6.873338, 21.299639, -22.073904, -6.873338, 17.195192, -22.165104, -6.539082, 18.221523, -22.073904, -6.6224284, 14.394604, -22.439577, -15.51292, 21.493383, -22.073904, -15.67263, 14.394604, -22.073904, -15.51292, 14.394604, -22.439577, -15.51292, 21.493383, -22.439577, -15.67263, 21.493383, -22.073904, -15.67263, 12.76609, -22.073904, -15.519905, 14.394604, -22.439577, -15.51292, 14.394604, -22.073904, -15.51292, 12.76609, -22.073904, -15.519905, 12.76609, -22.439577, -15.519905, 14.394604, -22.439577, -15.51292, 14.394604, -22.439577, -14.504482, 21.493383, -22.439577, -15.67263, 14.394604, -22.439577, -15.51292, 14.394604, -22.439577, -14.504482, 21.493383, -22.439577, -14.689938, 21.493383, -22.439577, -15.67263, 12.76609, -22.439577, -15.519905, 14.394604, -22.439577, -14.504482, 14.394604, -22.439577, -15.51292, 12.76609, -22.439577, -15.519905, 12.76609, -22.439577, -14.689938, 14.394604, -22.439577, -14.504482, 12.76609, -22.439577, -15.519905, 12.76609, -22.073904, -14.689938, 12.76609, -22.439577, -14.689938, 12.76609, -22.439577, -15.519905, 12.76609, -22.073904, -15.519905, 12.76609, -22.073904, -14.689938, 14.394604, -22.073904, -14.756702, 14.394604, -22.165104, -14.504482, 13.987476, -22.073904, -14.550737, 14.394604, -22.073904, -14.756702, 16.1693, -22.073904, -14.550737, 14.394604, -22.165104, -14.504482, 14.394604, -22.073904, -14.756702, 20.162472, -22.073904, -14.655029, 16.1693, -22.073904, -14.550737, 13.987476, -22.073904, -14.550737, 14.394604, -22.073904, -15.51292, 14.394604, -22.073904, -14.756702, 13.987476, -22.073904, -14.550737, 12.76609, -22.073904, -15.519905, 14.394604, -22.073904, -15.51292, 12.76609, -22.073904, -15.519905, 13.987476, -22.073904, -14.550737, 12.76609, -22.073904, -14.689938, 21.493383, -22.073904, -15.67263, 14.394604, -22.073904, -14.756702, 14.394604, -22.073904, -15.51292, 14.394604, -22.073904, -14.756702, 21.493383, -22.073904, -15.67263, 20.162472, -22.073904, -14.655029, 20.162472, -22.073904, -14.655029, 21.493383, -22.073904, -15.67263, 21.493383, -22.073904, -14.935175, 20.162472, -22.073904, -14.655029, 21.493383, -22.073904, -14.935175, 21.493383, -22.165104, -14.689938, 12.76609, -22.073904, -14.689938, 14.394604, -22.439577, -14.504482, 12.76609, -22.439577, -14.689938, 14.394604, -22.439577, -14.504482, 12.76609, -22.073904, -14.689938, 13.987476, -22.073904, -14.550737, 14.394604, -22.439577, -14.504482, 13.987476, -22.073904, -14.550737, 14.394604, -22.165104, -14.504482, 14.394604, -22.165104, -14.504482, 21.493383, -22.439577, -14.689938, 14.394604, -22.439577, -14.504482, 14.394604, -22.165104, -14.504482, 20.162472, -22.073904, -14.655029, 21.493383, -22.439577, -14.689938, 21.493383, -22.439577, -14.689938, 20.162472, -22.073904, -14.655029, 21.493383, -22.165104, -14.689938, 20.162472, -22.073904, -14.655029, 14.394604, -22.165104, -14.504482, 16.1693, -22.073904, -14.550737, 21.493383, -22.165104, -14.689938, 21.493383, -22.439577, -15.67263, 21.493383, -22.439577, -14.689938, 21.493383, -22.439577, -15.67263, 21.493383, -22.165104, -14.689938, 21.493383, -22.073904, -14.935175, 21.493383, -22.439577, -15.67263, 21.493383, -22.073904, -14.935175, 21.493383, -22.073904, -15.67263, 14.33395, -22.439577, -8.868834, 21.590258, -22.073904, -9.028543, 14.33395, -22.073904, -8.868834, 14.33395, -22.439577, -8.868834, 21.590258, -22.439577, -9.028543, 21.590258, -22.073904, -9.028543, 12.669218, -22.073904, -8.875814, 14.33395, -22.439577, -8.868834, 14.33395, -22.073904, -8.868834, 12.669218, -22.073904, -8.875814, 12.669218, -22.439577, -8.875814, 14.33395, -22.439577, -8.868834, 14.33395, -22.439577, -7.860395, 21.590258, -22.439577, -9.028543, 14.33395, -22.439577, -8.868834, 14.33395, -22.439577, -7.860395, 21.590258, -22.439577, -8.045849, 21.590258, -22.439577, -9.028543, 12.669218, -22.439577, -8.875814, 14.33395, -22.439577, -7.860395, 14.33395, -22.439577, -8.868834, 12.669218, -22.439577, -8.875814, 12.669218, -22.439577, -8.045849, 14.33395, -22.439577, -7.860395, 12.669218, -22.439577, -8.875814, 12.669218, -22.073904, -8.045849, 12.669218, -22.439577, -8.045849, 12.669218, -22.439577, -8.875814, 12.669218, -22.073904, -8.875814, 12.669218, -22.073904, -8.045849, 14.33395, -22.073904, -8.112614, 14.33395, -22.165104, -7.860395, 13.917658, -22.073904, -7.906649, 14.33395, -22.073904, -8.112614, 16.147917, -22.073904, -7.906649, 14.33395, -22.165104, -7.860395, 14.33395, -22.073904, -8.112614, 20.229671, -22.073904, -8.011377, 16.147917, -22.073904, -7.906649, 13.917658, -22.073904, -7.906649, 14.33395, -22.073904, -8.868834, 14.33395, -22.073904, -8.112614, 13.917658, -22.073904, -7.906649, 12.669218, -22.073904, -8.875814, 14.33395, -22.073904, -8.868834, 12.669218, -22.073904, -8.875814, 13.917658, -22.073904, -7.906649, 12.669218, -22.073904, -8.045849, 21.590258, -22.073904, -9.028543, 14.33395, -22.073904, -8.112614, 14.33395, -22.073904, -8.868834, 14.33395, -22.073904, -8.112614, 21.590258, -22.073904, -9.028543, 20.229671, -22.073904, -8.011377, 20.229671, -22.073904, -8.011377, 21.590258, -22.073904, -9.028543, 21.590258, -22.073904, -8.29196, 20.229671, -22.073904, -8.011377, 21.590258, -22.073904, -8.29196, 21.590258, -22.165104, -8.045849, 12.669218, -22.073904, -8.045849, 14.33395, -22.439577, -7.860395, 12.669218, -22.439577, -8.045849, 14.33395, -22.439577, -7.860395, 12.669218, -22.073904, -8.045849, 13.917658, -22.073904, -7.906649, 14.33395, -22.439577, -7.860395, 13.917658, -22.073904, -7.906649, 14.33395, -22.165104, -7.860395, 14.33395, -22.165104, -7.860395, 21.590258, -22.439577, -8.045849, 14.33395, -22.439577, -7.860395, 14.33395, -22.165104, -7.860395, 20.229671, -22.073904, -8.011377, 21.590258, -22.439577, -8.045849, 21.590258, -22.439577, -8.045849, 20.229671, -22.073904, -8.011377, 21.590258, -22.165104, -8.045849, 20.229671, -22.073904, -8.011377, 14.33395, -22.165104, -7.860395, 16.147917, -22.073904, -7.906649, 21.590258, -22.165104, -8.045849, 21.590258, -22.439577, -9.028543, 21.590258, -22.439577, -8.045849, 21.590258, -22.439577, -9.028543, 21.590258, -22.165104, -8.045849, 21.590258, -22.073904, -8.29196, 21.590258, -22.439577, -9.028543, 21.590258, -22.073904, -8.29196, 21.590258, -22.073904, -9.028543, 20.608437, -25.90344, 5.6272016, 21.338474, -21.174994, 5.6272016, 21.338474, -25.90344, 5.6272016, 20.608437, -25.90344, 5.6272016, 20.608437, -21.723503, 5.6272016, 21.338474, -21.174994, 5.6272016, 20.608437, -21.723503, 5.6272016, 21.338474, -21.174994, 4.8975997, 21.338474, -21.174994, 5.6272016, 20.608437, -21.723503, 5.6272016, 20.608437, -21.174994, 4.8975997, 21.338474, -21.174994, 4.8975997, 20.608437, -21.174994, 4.8975997, 21.338474, -25.90344, 4.8975997, 21.338474, -21.174994, 4.8975997, 20.608437, -21.174994, 4.8975997, 20.608437, -25.90344, 4.8975997, 21.338474, -25.90344, 4.8975997, 20.608437, -25.90344, 4.8975997, 21.338474, -25.90344, 5.6272016, 21.338474, -25.90344, 4.8975997, 20.608437, -25.90344, 4.8975997, 20.608437, -25.90344, 5.6272016, 21.338474, -25.90344, 5.6272016, 21.338474, -25.90344, 5.6272016, 21.338474, -21.174994, 4.8975997, 21.338474, -25.90344, 4.8975997, 21.338474, -25.90344, 5.6272016, 21.338474, -21.174994, 5.6272016, 21.338474, -21.174994, 4.8975997, 20.608437, -25.90344, 4.8975997, 20.608437, -21.723503, 5.6272016, 20.608437, -25.90344, 5.6272016, 20.608437, -25.90344, 4.8975997, 20.608437, -21.174994, 4.8975997, 20.608437, -21.723503, 5.6272016, 20.752874, -22.635942, 4.8980365, 20.984583, -22.543432, 4.8980365, 20.984583, -22.635942, 4.8980365, 20.752874, -22.635942, 4.8980365, 20.752874, -22.543432, 4.8980365, 20.984583, -22.543432, 4.8980365, 20.752874, -22.543432, 4.8980365, 20.984583, -22.543432, -19.742168, 20.984583, -22.543432, 4.8980365, 20.752874, -22.543432, 4.8980365, 20.752874, -22.543432, -19.742168, 20.984583, -22.543432, -19.742168, 20.752874, -22.543432, -19.742168, 20.984583, -22.635942, -19.742168, 20.984583, -22.543432, -19.742168, 20.752874, -22.543432, -19.742168, 20.752874, -22.635942, -19.742168, 20.984583, -22.635942, -19.742168, 20.752874, -22.635942, -19.742168, 20.984583, -22.635942, 4.8980365, 20.984583, -22.635942, -19.742168, 20.752874, -22.635942, -19.742168, 20.752874, -22.635942, 4.8980365, 20.984583, -22.635942, 4.8980365, 20.984583, -22.635942, 4.8980365, 20.984583, -22.543432, -19.742168, 20.984583, -22.635942, -19.742168, 20.984583, -22.635942, 4.8980365, 20.984583, -22.543432, 4.8980365, 20.984583, -22.543432, -19.742168, 20.752874, -22.635942, -19.742168, 20.752874, -22.543432, 4.8980365, 20.752874, -22.635942, 4.8980365, 20.752874, -22.635942, -19.742168, 20.752874, -22.543432, -19.742168, 20.752874, -22.543432, 4.8980365, 12.886963, -25.348385, -5.927299, 13.6170025, -20.619938, -5.927299, 13.6170025, -25.348385, -5.927299, 12.886963, -25.348385, -5.927299, 12.886963, -20.619938, -5.927299, 13.6170025, -20.619938, -5.927299, 12.886963, -25.348385, -6.6573377, 13.6170025, -25.348385, -5.927299, 13.6170025, -25.348385, -6.6573377, 12.886963, -25.348385, -6.6573377, 12.886963, -25.348385, -5.927299, 13.6170025, -25.348385, -5.927299, 13.6170025, -25.348385, -5.927299, 13.6170025, -20.619938, -6.6573377, 13.6170025, -25.348385, -6.6573377, 13.6170025, -25.348385, -5.927299, 13.6170025, -20.619938, -5.927299, 13.6170025, -20.619938, -6.6573377, 12.886963, -21.80205, -6.6573377, 13.069365, -20.619938, -6.6573377, 12.886963, -20.619938, -6.474937, 12.886963, -25.348385, -5.927299, 12.886963, -21.80205, -6.6573377, 12.886963, -20.619938, -6.474937, 12.886963, -21.80205, -6.6573377, 12.886963, -25.348385, -5.927299, 12.886963, -25.348385, -6.6573377, 12.886963, -25.348385, -5.927299, 12.886963, -20.619938, -6.474937, 12.886963, -20.619938, -5.927299, 12.886963, -20.619938, -6.474937, 13.6170025, -20.619938, -5.927299, 12.886963, -20.619938, -5.927299, 13.6170025, -20.619938, -5.927299, 12.886963, -20.619938, -6.474937, 13.069365, -20.619938, -6.6573377, 13.6170025, -20.619938, -5.927299, 13.069365, -20.619938, -6.6573377, 13.6170025, -20.619938, -6.6573377, 12.886963, -25.348385, -6.6573377, 13.069365, -20.619938, -6.6573377, 12.886963, -21.80205, -6.6573377, 13.069365, -20.619938, -6.6573377, 12.886963, -25.348385, -6.6573377, 13.6170025, -25.348385, -6.6573377, 13.069365, -20.619938, -6.6573377, 13.6170025, -25.348385, -6.6573377, 13.6170025, -20.619938, -6.6573377, 12.886963, -26.235077, -13.537062, 13.6170025, -21.50663, -13.537062, 13.6170025, -26.235077, -13.537062, 12.886963, -26.235077, -13.537062, 12.886963, -21.50663, -13.537062, 13.6170025, -21.50663, -13.537062, 12.886963, -26.235077, -14.2666645, 13.6170025, -26.235077, -13.537062, 13.6170025, -26.235077, -14.2666645, 12.886963, -26.235077, -14.2666645, 12.886963, -26.235077, -13.537062, 13.6170025, -26.235077, -13.537062, 13.6170025, -26.235077, -13.537062, 13.6170025, -21.50663, -14.2666645, 13.6170025, -26.235077, -14.2666645, 13.6170025, -26.235077, -13.537062, 13.6170025, -21.50663, -13.537062, 13.6170025, -21.50663, -14.2666645, 12.886963, -22.688742, -14.2666645, 13.069365, -21.50663, -14.2666645, 12.886963, -21.50663, -14.084265, 12.886963, -26.235077, -13.537062, 12.886963, -22.688742, -14.2666645, 12.886963, -21.50663, -14.084265, 12.886963, -22.688742, -14.2666645, 12.886963, -26.235077, -13.537062, 12.886963, -26.235077, -14.2666645, 12.886963, -26.235077, -13.537062, 12.886963, -21.50663, -14.084265, 12.886963, -21.50663, -13.537062, 12.886963, -21.50663, -14.084265, 13.6170025, -21.50663, -13.537062, 12.886963, -21.50663, -13.537062, 13.6170025, -21.50663, -13.537062, 12.886963, -21.50663, -14.084265, 13.069365, -21.50663, -14.2666645, 13.6170025, -21.50663, -13.537062, 13.069365, -21.50663, -14.2666645, 13.6170025, -21.50663, -14.2666645, 12.886963, -26.235077, -14.2666645, 13.069365, -21.50663, -14.2666645, 12.886963, -22.688742, -14.2666645, 13.069365, -21.50663, -14.2666645, 12.886963, -26.235077, -14.2666645, 13.6170025, -26.235077, -14.2666645, 13.069365, -21.50663, -14.2666645, 13.6170025, -26.235077, -14.2666645, 13.6170025, -21.50663, -14.2666645, 21.21673, -25.99464, -5.8286805, 21.946331, -21.266193, -5.8286805, 21.946331, -25.99464, -5.8286805, 21.21673, -25.99464, -5.8286805, 21.21673, -21.266193, -5.8286805, 21.946331, -21.266193, -5.8286805, 21.21673, -25.99464, -6.558719, 21.946331, -25.99464, -5.8286805, 21.946331, -25.99464, -6.558719, 21.21673, -25.99464, -6.558719, 21.21673, -25.99464, -5.8286805, 21.946331, -25.99464, -5.8286805, 21.946331, -25.99464, -5.8286805, 21.946331, -21.266193, -6.558719, 21.946331, -25.99464, -6.558719, 21.946331, -25.99464, -5.8286805, 21.946331, -21.266193, -5.8286805, 21.946331, -21.266193, -6.558719, 21.21673, -22.448305, -6.558719, 21.39913, -21.266193, -6.558719, 21.21673, -21.266193, -6.3763185, 21.21673, -25.99464, -5.8286805, 21.21673, -22.448305, -6.558719, 21.21673, -21.266193, -6.3763185, 21.21673, -22.448305, -6.558719, 21.21673, -25.99464, -5.8286805, 21.21673, -25.99464, -6.558719, 21.21673, -25.99464, -5.8286805, 21.21673, -21.266193, -6.3763185, 21.21673, -21.266193, -5.8286805, 21.21673, -21.266193, -6.3763185, 21.946331, -21.266193, -5.8286805, 21.21673, -21.266193, -5.8286805, 21.946331, -21.266193, -5.8286805, 21.21673, -21.266193, -6.3763185, 21.39913, -21.266193, -6.558719, 21.946331, -21.266193, -5.8286805, 21.39913, -21.266193, -6.558719, 21.946331, -21.266193, -6.558719, 21.21673, -25.99464, -6.558719, 21.39913, -21.266193, -6.558719, 21.21673, -22.448305, -6.558719, 21.39913, -21.266193, -6.558719, 21.21673, -25.99464, -6.558719, 21.946331, -25.99464, -6.558719, 21.39913, -21.266193, -6.558719, 21.946331, -25.99464, -6.558719, 21.946331, -21.266193, -6.558719, 21.21673, -25.99464, -13.537062, 21.946331, -21.266193, -13.537062, 21.946331, -25.99464, -13.537062, 21.21673, -25.99464, -13.537062, 21.21673, -21.266193, -13.537062, 21.946331, -21.266193, -13.537062, 21.21673, -25.99464, -14.2666645, 21.946331, -25.99464, -13.537062, 21.946331, -25.99464, -14.2666645, 21.21673, -25.99464, -14.2666645, 21.21673, -25.99464, -13.537062, 21.946331, -25.99464, -13.537062, 21.946331, -25.99464, -13.537062, 21.946331, -21.266193, -14.2666645, 21.946331, -25.99464, -14.2666645, 21.946331, -25.99464, -13.537062, 21.946331, -21.266193, -13.537062, 21.946331, -21.266193, -14.2666645, 21.21673, -22.448305, -14.2666645, 21.39913, -21.266193, -14.2666645, 21.21673, -21.266193, -14.084265, 21.21673, -25.99464, -13.537062, 21.21673, -22.448305, -14.2666645, 21.21673, -21.266193, -14.084265, 21.21673, -22.448305, -14.2666645, 21.21673, -25.99464, -13.537062, 21.21673, -25.99464, -14.2666645, 21.21673, -25.99464, -13.537062, 21.21673, -21.266193, -14.084265, 21.21673, -21.266193, -13.537062, 21.21673, -21.266193, -14.084265, 21.946331, -21.266193, -13.537062, 21.21673, -21.266193, -13.537062, 21.946331, -21.266193, -13.537062, 21.21673, -21.266193, -14.084265, 21.39913, -21.266193, -14.2666645, 21.946331, -21.266193, -13.537062, 21.39913, -21.266193, -14.2666645, 21.946331, -21.266193, -14.2666645, 21.21673, -25.99464, -14.2666645, 21.39913, -21.266193, -14.2666645, 21.21673, -22.448305, -14.2666645, 21.39913, -21.266193, -14.2666645, 21.21673, -25.99464, -14.2666645, 21.946331, -25.99464, -14.2666645, 21.39913, -21.266193, -14.2666645, 21.946331, -25.99464, -14.2666645, 21.946331, -21.266193, -14.2666645, -13.073534, -0.35833585, 29.034882, -8.669889, -0.18640822, 28.876835, -13.057354, 0.007337749, 29.029753, -13.073534, -0.35833585, 29.034882, -8.686073, -0.5516454, 28.882399, -8.669889, -0.18640822, 28.876835, -17.582396, 0.2010836, 28.810827, -13.073534, -0.35833585, 29.034882, -13.057354, 0.007337749, 29.029753, -17.582396, 0.2010836, 28.810827, -17.59858, -0.16415358, 28.816393, -13.073534, -0.35833585, 29.034882, -13.080118, -0.3434996, 30.014496, -8.686073, -0.5516454, 28.882399, -13.073534, -0.35833585, 29.034882, -13.080118, -0.3434996, 30.014496, -8.69165, -0.538991, 29.712345, -8.686073, -0.5516454, 28.882399, -13.073534, -0.35833585, 29.034882, -17.606195, -0.1471355, 29.949606, -13.080118, -0.3434996, 30.014496, -13.073534, -0.35833585, 29.034882, -17.602324, -0.15586269, 29.373617, -17.606195, -0.1471355, 29.949606, -17.602324, -0.15586269, 29.373617, -13.073534, -0.35833585, 29.034882, -17.59858, -0.16415358, 28.816393, -8.69165, -0.538991, 29.712345, -8.669889, -0.18640822, 28.876835, -8.686073, -0.5516454, 28.882399, -8.69165, -0.538991, 29.712345, -8.675467, -0.17375374, 29.706781, -8.669889, -0.18640822, 28.876835, -17.585695, 0.20850176, 29.301727, -17.420107, 0.093301594, 29.40146, -17.602324, -0.15586269, 29.373617, -13.06229, 0.01824683, 29.764137, -13.067871, -0.06946254, 30.010216, -14.195347, 0.07104677, 29.993038, -13.06229, 0.01824683, 29.764137, -11.966818, -0.027135193, 29.933395, -13.067871, -0.06946254, 30.010216, -13.06229, 0.01824683, 29.764137, -8.675467, -0.17375374, 29.706781, -11.966818, -0.027135193, 29.933395, -13.06229, 0.01824683, 29.764137, -8.669889, -0.18640822, 28.876835, -8.675467, -0.17375374, 29.706781, -8.669889, -0.18640822, 28.876835, -13.06229, 0.01824683, 29.764137, -13.057354, 0.007337749, 29.029753, -14.195347, 0.07104677, 29.993038, -13.057354, 0.007337749, 29.029753, -13.06229, 0.01824683, 29.764137, -13.057354, 0.007337749, 29.029753, -17.585695, 0.20850176, 29.301727, -17.582396, 0.2010836, 28.810827, -13.057354, 0.007337749, 29.029753, -16.80835, 0.17621082, 29.407318, -17.585695, 0.20850176, 29.301727, -14.195347, 0.07104677, 29.993038, -16.80835, 0.17621082, 29.407318, -13.057354, 0.007337749, 29.029753, -17.420107, 0.093301594, 29.40146, -17.585695, 0.20850176, 29.301727, -16.80835, 0.17621082, 29.407318, -17.587553, 0.21286553, 29.57794, -17.420107, 0.093301594, 29.40146, -16.80835, 0.17621082, 29.407318, -16.80835, 0.17621082, 29.407318, -14.195347, 0.07104677, 29.993038, -17.587553, 0.21286553, 29.57794, -17.587553, 0.21286553, 29.57794, -14.195347, 0.07104677, 29.993038, -17.590448, 0.21810168, 29.944038, -17.59858, -0.16415358, 28.816393, -17.585695, 0.20850176, 29.301727, -17.602324, -0.15586269, 29.373617, -17.59858, -0.16415358, 28.816393, -17.582396, 0.2010836, 28.810827, -17.585695, 0.20850176, 29.301727, -17.587553, 0.21286553, 29.57794, -17.606195, -0.1471355, 29.949606, -17.602324, -0.15586269, 29.373617, -17.587553, 0.21286553, 29.57794, -17.590448, 0.21810168, 29.944038, -17.606195, -0.1471355, 29.949606, -17.420107, 0.093301594, 29.40146, -17.587553, 0.21286553, 29.57794, -17.602324, -0.15586269, 29.373617, -17.590448, 0.21810168, 29.944038, -13.080118, -0.3434996, 30.014496, -17.606195, -0.1471355, 29.949606, -13.080118, -0.3434996, 30.014496, -17.590448, 0.21810168, 29.944038, -13.067871, -0.06946254, 30.010216, -13.067871, -0.06946254, 30.010216, -17.590448, 0.21810168, 29.944038, -14.195347, 0.07104677, 29.993038, -13.067871, -0.06946254, 30.010216, -8.675467, -0.17375374, 29.706781, -13.080118, -0.3434996, 30.014496, -13.080118, -0.3434996, 30.014496, -8.675467, -0.17375374, 29.706781, -8.69165, -0.538991, 29.712345, -8.675467, -0.17375374, 29.706781, -13.067871, -0.06946254, 30.010216, -11.966818, -0.027135193, 29.933395, -9.878, -0.097390115, 31.312849, -8.993242, -0.5014638, 31.344105, -9.893747, -0.4626273, 31.318417, -9.878, -0.097390115, 31.312849, -8.977062, -0.13622653, 31.338978, -8.993242, -0.5014638, 31.344105, -9.893747, -0.4626273, 31.318417, -17.31272, 0.1343196, 31.278593, -9.878, -0.097390115, 31.312849, -9.878, -0.097390115, 31.312849, -17.31272, 0.1343196, 31.278593, -15.451195, 0.14479238, 31.285868, -17.31272, 0.1343196, 31.278593, -9.893747, -0.4626273, 31.318417, -17.324528, -0.13971746, 31.28244, -8.970457, -0.15106285, 30.356306, -9.878, -0.097390115, 31.312849, -9.871372, -0.11222637, 30.326687, -15.451195, 0.14479238, 31.285868, -9.871372, -0.11222637, 30.326687, -9.878, -0.097390115, 31.312849, -15.451195, 0.14479238, 31.285868, -17.303205, 0.21330172, 30.447365, -9.871372, -0.11222637, 30.326687, -17.303205, 0.21330172, 30.447365, -15.451195, 0.14479238, 31.285868, -17.307388, 0.22246522, 31.069607, -17.307388, 0.22246522, 31.069607, -15.451195, 0.14479238, 31.285868, -17.31272, 0.1343196, 31.278593, -9.878, -0.097390115, 31.312849, -8.970457, -0.15106285, 30.356306, -9.454359, -0.122262776, 30.87016, -9.454359, -0.122262776, 30.87016, -8.970457, -0.15106285, 30.356306, -8.973118, -0.14495373, 30.75208, -8.973118, -0.14495373, 30.75208, -9.062445, -0.27935404, 30.862755, -9.454359, -0.122262776, 30.87016, -9.062445, -0.27935404, 30.862755, -8.974958, -0.14102644, 31.026112, -9.454359, -0.122262776, 30.87016, -9.878, -0.097390115, 31.312849, -9.454359, -0.122262776, 30.87016, -8.974958, -0.14102644, 31.026112, -9.878, -0.097390115, 31.312849, -8.974958, -0.14102644, 31.026112, -8.977062, -0.13622653, 31.338978, -9.8871155, -0.47789997, 30.331818, -8.970457, -0.15106285, 30.356306, -9.871372, -0.11222637, 30.326687, -9.8871155, -0.47789997, 30.331818, -8.986637, -0.5163001, 30.361433, -8.970457, -0.15106285, 30.356306, -17.303205, 0.21330172, 30.447365, -9.8871155, -0.47789997, 30.331818, -9.871372, -0.11222637, 30.326687, -17.303205, 0.21330172, 30.447365, -17.318953, -0.15193558, 30.45293, -9.8871155, -0.47789997, 30.331818, -8.993242, -0.5014638, 31.344105, -9.8871155, -0.47789997, 30.331818, -9.893747, -0.4626273, 31.318417, -9.8871155, -0.47789997, 30.331818, -8.993242, -0.5014638, 31.344105, -8.989674, -0.50975466, 30.81306, -9.8871155, -0.47789997, 30.331818, -8.989674, -0.50975466, 30.81306, -8.986637, -0.5163001, 30.361433, -17.318953, -0.15193558, 30.45293, -9.893747, -0.4626273, 31.318417, -9.8871155, -0.47789997, 30.331818, -17.318953, -0.15193558, 30.45293, -17.324528, -0.13971746, 31.28244, -9.893747, -0.4626273, 31.318417, -8.974958, -0.14102644, 31.026112, -9.062445, -0.27935404, 30.862755, -8.989674, -0.50975466, 30.81306, -9.062445, -0.27935404, 30.862755, -8.973118, -0.14495373, 30.75208, -8.989674, -0.50975466, 30.81306, -8.993242, -0.5014638, 31.344105, -8.974958, -0.14102644, 31.026112, -8.989674, -0.50975466, 30.81306, -8.993242, -0.5014638, 31.344105, -8.977062, -0.13622653, 31.338978, -8.974958, -0.14102644, 31.026112, -8.973118, -0.14495373, 30.75208, -8.986637, -0.5163001, 30.361433, -8.989674, -0.50975466, 30.81306, -8.973118, -0.14495373, 30.75208, -8.970457, -0.15106285, 30.356306, -8.986637, -0.5163001, 30.361433, -17.303205, 0.21330172, 30.447365, -17.324528, -0.13971746, 31.28244, -17.318953, -0.15193558, 30.45293, -17.324528, -0.13971746, 31.28244, -17.303205, 0.21330172, 30.447365, -17.31272, 0.1343196, 31.278593, -17.31272, 0.1343196, 31.278593, -17.303205, 0.21330172, 30.447365, -17.307388, 0.22246522, 31.069607, -13.105944, -0.29549986, 33.143127, -8.988548, -0.111353934, 32.98316, -13.089764, 0.069737375, 33.138, -13.105944, -0.29549986, 33.143127, -9.004295, -0.47659117, 32.988728, -8.988548, -0.111353934, 32.98316, -17.31983, 0.25082868, 32.92106, -13.105944, -0.29549986, 33.143127, -13.089764, 0.069737375, 33.138, -17.31983, 0.25082868, 32.92106, -17.335577, -0.11440843, 32.926624, -13.105944, -0.29549986, 33.143127, -13.112524, -0.2806636, 34.12231, -9.004295, -0.47659117, 32.988728, -13.105944, -0.29549986, 33.143127, -13.112524, -0.2806636, 34.12231, -9.009869, -0.4639367, 33.818237, -9.004295, -0.47659117, 32.988728, -13.105944, -0.29549986, 33.143127, -17.343197, -0.09739047, 34.060276, -13.112524, -0.2806636, 34.12231, -13.105944, -0.29549986, 33.143127, -17.339323, -0.10611755, 33.483852, -17.343197, -0.09739047, 34.060276, -17.339323, -0.10611755, 33.483852, -13.105944, -0.29549986, 33.143127, -17.335577, -0.11440843, 32.926624, -9.009869, -0.4639367, 33.818237, -8.988548, -0.111353934, 32.98316, -9.004295, -0.47659117, 32.988728, -9.009869, -0.4639367, 33.818237, -8.994122, -0.09869945, 33.812668, -8.988548, -0.111353934, 32.98316, -17.323126, 0.25824696, 33.411522, -17.16845, 0.14348298, 33.51162, -17.339323, -0.10611755, 33.483852, -13.0947, 0.080646336, 33.872387, -13.100277, -0.0066265464, 34.118027, -14.154011, 0.1303919, 34.101345, -13.0947, 0.080646336, 33.872387, -12.070787, 0.038755357, 34.040726, -13.100277, -0.0066265464, 34.118027, -13.0947, 0.080646336, 33.872387, -8.994122, -0.09869945, 33.812668, -12.070787, 0.038755357, 34.040726, -13.0947, 0.080646336, 33.872387, -8.988548, -0.111353934, 32.98316, -8.994122, -0.09869945, 33.812668, -8.988548, -0.111353934, 32.98316, -13.0947, 0.080646336, 33.872387, -13.089764, 0.069737375, 33.138, -14.154011, 0.1303919, 34.101345, -13.089764, 0.069737375, 33.138, -13.0947, 0.080646336, 33.872387, -13.089764, 0.069737375, 33.138, -17.323126, 0.25824696, 33.411522, -17.31983, 0.25082868, 32.92106, -13.089764, 0.069737375, 33.138, -16.596401, 0.22813767, 33.51721, -17.323126, 0.25824696, 33.411522, -14.154011, 0.1303919, 34.101345, -16.596401, 0.22813767, 33.51721, -13.089764, 0.069737375, 33.138, -17.16845, 0.14348298, 33.51162, -17.323126, 0.25824696, 33.411522, -16.596401, 0.22813767, 33.51721, -17.324986, 0.2621742, 33.68817, -17.16845, 0.14348298, 33.51162, -16.596401, 0.22813767, 33.51721, -16.596401, 0.22813767, 33.51721, -14.154011, 0.1303919, 34.101345, -17.324986, 0.2621742, 33.68817, -17.324986, 0.2621742, 33.68817, -14.154011, 0.1303919, 34.101345, -17.32745, 0.26784688, 34.05471, -17.335577, -0.11440843, 32.926624, -17.323126, 0.25824696, 33.411522, -17.339323, -0.10611755, 33.483852, -17.335577, -0.11440843, 32.926624, -17.31983, 0.25082868, 32.92106, -17.323126, 0.25824696, 33.411522, -17.324986, 0.2621742, 33.68817, -17.343197, -0.09739047, 34.060276, -17.339323, -0.10611755, 33.483852, -17.324986, 0.2621742, 33.68817, -17.32745, 0.26784688, 34.05471, -17.343197, -0.09739047, 34.060276, -17.16845, 0.14348298, 33.51162, -17.324986, 0.2621742, 33.68817, -17.339323, -0.10611755, 33.483852, -17.32745, 0.26784688, 34.05471, -13.112524, -0.2806636, 34.12231, -17.343197, -0.09739047, 34.060276, -13.112524, -0.2806636, 34.12231, -17.32745, 0.26784688, 34.05471, -13.100277, -0.0066265464, 34.118027, -13.100277, -0.0066265464, 34.118027, -17.32745, 0.26784688, 34.05471, -14.154011, 0.1303919, 34.101345, -13.100277, -0.0066265464, 34.118027, -8.994122, -0.09869945, 33.812668, -13.112524, -0.2806636, 34.12231, -13.112524, -0.2806636, 34.12231, -8.994122, -0.09869945, 33.812668, -9.009869, -0.4639367, 33.818237, -8.994122, -0.09869945, 33.812668, -13.100277, -0.0066265464, 34.118027, -12.070787, 0.038755357, 34.040726, -15.954979, -0.19208127, 31.771221, -8.68903, -0.14408118, 31.659903, -15.939232, 0.17359239, 31.765654, -15.954979, -0.19208127, 31.771221, -8.704777, -0.50931835, 31.66547, -8.68903, -0.14408118, 31.659903, -17.602564, 0.24559253, 31.74662, -15.954979, -0.19208127, 31.771221, -15.939232, 0.17359239, 31.765654, -17.602564, 0.24559253, 31.74662, -17.61831, -0.11964476, 31.752188, -15.954979, -0.19208127, 31.771221, -15.961756, -0.1768086, 32.779636, -8.704777, -0.50931835, 31.66547, -15.954979, -0.19208127, 31.771221, -15.961756, -0.1768086, 32.779636, -8.71138, -0.49448207, 32.64814, -8.704777, -0.50931835, 31.66547, -17.61831, -0.11964476, 31.752188, -15.961756, -0.1768086, 32.779636, -15.954979, -0.19208127, 31.771221, -17.61831, -0.11964476, 31.752188, -17.623884, -0.10699028, 32.5817, -15.961756, -0.1768086, 32.779636, -17.61831, -0.11964476, 31.752188, -17.608137, 0.25824708, 32.57613, -17.623884, -0.10699028, 32.5817, -17.61831, -0.11964476, 31.752188, -17.602564, 0.24559253, 31.74662, -17.608137, 0.25824708, 32.57613, -15.944313, 0.18493778, 32.52186, -15.949944, 0.09722847, 32.775352, -16.361544, 0.20588332, 32.72502, -15.944313, 0.18493778, 32.52186, -14.133524, 0.109446704, 32.741302, -15.949944, 0.09722847, 32.775352, -15.944313, 0.18493778, 32.52186, -10.055107, -0.069462895, 32.667473, -14.133524, 0.109446704, 32.741302, -16.361544, 0.20588332, 32.72502, -15.939232, 0.17359239, 31.765654, -15.944313, 0.18493778, 32.52186, -16.361544, 0.20588332, 32.72502, -17.602564, 0.24559253, 31.74662, -15.939232, 0.17359239, 31.765654, -17.602564, 0.24559253, 31.74662, -16.361544, 0.20588332, 32.72502, -17.608137, 0.25824708, 32.57613, -8.68903, -0.14408118, 31.659903, -15.944313, 0.18493778, 32.52186, -15.939232, 0.17359239, 31.765654, -15.944313, 0.18493778, 32.52186, -8.68903, -0.14408118, 31.659903, -10.055107, -0.069462895, 32.667473, -10.055107, -0.069462895, 32.667473, -8.68903, -0.14408118, 31.659903, -8.693983, -0.13273573, 32.396904, -10.055107, -0.069462895, 32.667473, -8.693983, -0.13273573, 32.396904, -8.69957, -0.2204451, 32.643856, -17.608137, 0.25824708, 32.57613, -15.961756, -0.1768086, 32.779636, -17.623884, -0.10699028, 32.5817, -15.961756, -0.1768086, 32.779636, -17.608137, 0.25824708, 32.57613, -15.949944, 0.09722847, 32.775352, -15.949944, 0.09722847, 32.775352, -17.608137, 0.25824708, 32.57613, -16.361544, 0.20588332, 32.72502, -15.949944, 0.09722847, 32.775352, -8.69957, -0.2204451, 32.643856, -15.961756, -0.1768086, 32.779636, -15.961756, -0.1768086, 32.779636, -8.69957, -0.2204451, 32.643856, -8.71138, -0.49448207, 32.64814, -8.69957, -0.2204451, 32.643856, -15.949944, 0.09722847, 32.775352, -10.055107, -0.069462895, 32.667473, -10.055107, -0.069462895, 32.667473, -15.949944, 0.09722847, 32.775352, -14.133524, 0.109446704, 32.741302, -8.69957, -0.2204451, 32.643856, -8.704777, -0.50931835, 31.66547, -8.71138, -0.49448207, 32.64814, -8.704777, -0.50931835, 31.66547, -8.69957, -0.2204451, 32.643856, -8.68903, -0.14408118, 31.659903, -8.68903, -0.14408118, 31.659903, -8.69957, -0.2204451, 32.643856, -8.693983, -0.13273573, 32.396904, -13.166946, -0.5581908, 28.64937, -17.27211, -0.19208091, 28.80724, -13.166946, -0.19208091, 28.64937, -13.166946, -0.5581908, 28.64937, -17.27211, -0.5581909, 28.80724, -17.27211, -0.19208091, 28.80724, -8.932932, -0.19208091, 28.863285, -13.166946, -0.5581908, 28.64937, -13.166946, -0.19208091, 28.64937, -8.932932, -0.19208091, 28.863285, -8.932932, -0.5581909, 28.863285, -13.166946, -0.5581908, 28.64937, -13.160368, -0.5581907, 27.670628, -17.27211, -0.5581909, 28.80724, -13.166946, -0.5581908, 28.64937, -13.160368, -0.5581907, 27.670628, -17.266533, -0.5581908, 27.977295, -17.27211, -0.5581909, 28.80724, -13.166946, -0.5581908, 28.64937, -8.925314, -0.55819076, 27.729633, -13.160368, -0.5581907, 27.670628, -13.166946, -0.5581908, 28.64937, -8.929188, -0.5581908, 28.306059, -8.925314, -0.55819076, 27.729633, -8.929188, -0.5581908, 28.306059, -13.166946, -0.5581908, 28.64937, -8.932932, -0.5581909, 28.863285, -17.266533, -0.5581908, 27.977295, -17.27211, -0.19208091, 28.80724, -17.27211, -0.5581909, 28.80724, -17.266533, -0.5581908, 27.977295, -17.266533, -0.1920808, 27.977295, -17.27211, -0.19208091, 28.80724, -8.929636, -0.19208086, 28.372822, -9.089121, -0.30160832, 28.274002, -8.929188, -0.5581908, 28.306059, -13.16201, -0.1920808, 27.914986, -13.160368, -0.28371733, 27.670628, -12.101822, -0.1920808, 27.685158, -13.16201, -0.1920808, 27.914986, -14.1868, -0.1920808, 27.747076, -13.160368, -0.28371733, 27.670628, -13.16201, -0.1920808, 27.914986, -17.266533, -0.1920808, 27.977295, -14.1868, -0.1920808, 27.747076, -13.16201, -0.1920808, 27.914986, -17.27211, -0.19208091, 28.80724, -17.266533, -0.1920808, 27.977295, -17.27211, -0.19208091, 28.80724, -13.16201, -0.1920808, 27.914986, -13.166946, -0.19208091, 28.64937, -12.101822, -0.1920808, 27.685158, -13.166946, -0.19208091, 28.64937, -13.16201, -0.1920808, 27.914986, -13.166946, -0.19208091, 28.64937, -8.929636, -0.19208086, 28.372822, -8.932932, -0.19208091, 28.863285, -13.166946, -0.19208091, 28.64937, -9.656805, -0.19208086, 28.268005, -8.929636, -0.19208086, 28.372822, -12.101822, -0.1920808, 27.685158, -9.656805, -0.19208086, 28.268005, -13.166946, -0.19208091, 28.64937, -9.089121, -0.30160832, 28.274002, -8.929636, -0.19208086, 28.372822, -9.656805, -0.19208086, 28.268005, -8.92778, -0.19208086, 28.09661, -9.089121, -0.30160832, 28.274002, -9.656805, -0.19208086, 28.268005, -9.656805, -0.19208086, 28.268005, -12.101822, -0.1920808, 27.685158, -8.92778, -0.19208086, 28.09661, -8.92778, -0.19208086, 28.09661, -12.101822, -0.1920808, 27.685158, -8.925314, -0.1920808, 27.729633, -8.932932, -0.5581909, 28.863285, -8.929636, -0.19208086, 28.372822, -8.929188, -0.5581908, 28.306059, -8.932932, -0.5581909, 28.863285, -8.932932, -0.19208091, 28.863285, -8.929636, -0.19208086, 28.372822, -8.92778, -0.19208086, 28.09661, -8.925314, -0.55819076, 27.729633, -8.929188, -0.5581908, 28.306059, -8.92778, -0.19208086, 28.09661, -8.925314, -0.1920808, 27.729633, -8.925314, -0.55819076, 27.729633, -9.089121, -0.30160832, 28.274002, -8.92778, -0.19208086, 28.09661, -8.929188, -0.5581908, 28.306059, -8.925314, -0.1920808, 27.729633, -13.160368, -0.5581907, 27.670628, -8.925314, -0.55819076, 27.729633, -13.160368, -0.5581907, 27.670628, -8.925314, -0.1920808, 27.729633, -12.101822, -0.1920808, 27.685158, -13.160368, -0.5581907, 27.670628, -12.101822, -0.1920808, 27.685158, -13.160368, -0.28371733, 27.670628, -13.160368, -0.28371733, 27.670628, -17.266533, -0.5581908, 27.977295, -13.160368, -0.5581907, 27.670628, -13.160368, -0.28371733, 27.670628, -17.266533, -0.1920808, 27.977295, -17.266533, -0.5581908, 27.977295, -17.266533, -0.1920808, 27.977295, -13.160368, -0.28371733, 27.670628, -14.1868, -0.1920808, 27.747076, -16.346624, -0.1920805, 25.245663, -17.248005, -0.55819046, 25.220404, -16.346624, -0.55819046, 25.245663, -16.346624, -0.1920805, 25.245663, -17.248005, -0.1920805, 25.220404, -17.248005, -0.55819046, 25.220404, -16.346624, -0.55819046, 25.245663, -10.768602, -0.1920805, 25.26875, -16.346624, -0.1920805, 25.245663, -16.346624, -0.55819046, 25.245663, -8.908827, -0.28371704, 25.276447, -10.768602, -0.1920805, 25.26875, -8.908827, -0.28371704, 25.276447, -16.346624, -0.55819046, 25.245663, -8.908827, -0.55819046, 25.276447, -17.254608, -0.19208062, 26.203075, -16.346624, -0.1920805, 25.245663, -16.353252, -0.19208062, 26.231823, -10.768602, -0.1920805, 25.26875, -16.353252, -0.19208062, 26.231823, -16.346624, -0.1920805, 25.245663, -10.768602, -0.1920805, 25.26875, -8.914402, -0.19208062, 26.105959, -16.353252, -0.19208062, 26.231823, -8.914402, -0.19208062, 26.105959, -10.768602, -0.1920805, 25.26875, -8.910217, -0.1920805, 25.48328, -8.910217, -0.1920805, 25.48328, -10.768602, -0.1920805, 25.26875, -8.908827, -0.28371704, 25.276447, -16.346624, -0.1920805, 25.245663, -17.254608, -0.19208062, 26.203075, -16.7707, -0.19208056, 25.68835, -16.7707, -0.19208056, 25.68835, -17.254608, -0.19208062, 26.203075, -17.251945, -0.19208056, 25.806864, -17.251945, -0.19208056, 25.806864, -17.169178, -0.33171725, 25.698326, -16.7707, -0.19208056, 25.68835, -17.169178, -0.33171725, 25.698326, -17.250105, -0.1920805, 25.532833, -16.7707, -0.19208056, 25.68835, -16.346624, -0.1920805, 25.245663, -16.7707, -0.19208056, 25.68835, -17.250105, -0.1920805, 25.532833, -16.346624, -0.1920805, 25.245663, -17.250105, -0.1920805, 25.532833, -17.248005, -0.1920805, 25.220404, -16.353252, -0.5581906, 26.231823, -17.254608, -0.19208062, 26.203075, -16.353252, -0.19208062, 26.231823, -16.353252, -0.5581906, 26.231823, -17.254608, -0.5581906, 26.203075, -17.254608, -0.19208062, 26.203075, -8.914402, -0.19208062, 26.105959, -16.353252, -0.5581906, 26.231823, -16.353252, -0.19208062, 26.231823, -8.914402, -0.19208062, 26.105959, -8.914402, -0.5581906, 26.105959, -16.353252, -0.5581906, 26.231823, -17.248005, -0.55819046, 25.220404, -16.353252, -0.5581906, 26.231823, -16.346624, -0.55819046, 25.245663, -16.353252, -0.5581906, 26.231823, -17.248005, -0.55819046, 25.220404, -17.251574, -0.5581905, 25.751448, -16.353252, -0.5581906, 26.231823, -17.251574, -0.5581905, 25.751448, -17.254608, -0.5581906, 26.203075, -8.914402, -0.5581906, 26.105959, -16.346624, -0.55819046, 25.245663, -16.353252, -0.5581906, 26.231823, -8.914402, -0.5581906, 26.105959, -8.908827, -0.55819046, 25.276447, -16.346624, -0.55819046, 25.245663, -17.250105, -0.1920805, 25.532833, -17.169178, -0.33171725, 25.698326, -17.251574, -0.5581905, 25.751448, -17.169178, -0.33171725, 25.698326, -17.251945, -0.19208056, 25.806864, -17.251574, -0.5581905, 25.751448, -17.248005, -0.55819046, 25.220404, -17.250105, -0.1920805, 25.532833, -17.251574, -0.5581905, 25.751448, -17.248005, -0.55819046, 25.220404, -17.248005, -0.1920805, 25.220404, -17.250105, -0.1920805, 25.532833, -17.251945, -0.19208056, 25.806864, -17.254608, -0.5581906, 26.203075, -17.251574, -0.5581905, 25.751448, -17.251945, -0.19208056, 25.806864, -17.254608, -0.19208062, 26.203075, -17.254608, -0.5581906, 26.203075, -8.914402, -0.19208062, 26.105959, -8.908827, -0.55819046, 25.276447, -8.914402, -0.5581906, 26.105959, -8.908827, -0.55819046, 25.276447, -8.914402, -0.19208062, 26.105959, -8.910217, -0.1920805, 25.48328, -8.908827, -0.55819046, 25.276447, -8.910217, -0.1920805, 25.48328, -8.908827, -0.28371704, 25.276447, -16.566906, -0.19208038, 24.06379, -17.53112, -0.55819035, 24.038109, -16.566906, -0.55819035, 24.06379, -16.566906, -0.19208038, 24.06379, -17.53112, -0.19208038, 24.038109, -17.53112, -0.55819035, 24.038109, -16.566906, -0.55819035, 24.06379, -10.599218, -0.19208038, 24.089493, -16.566906, -0.19208038, 24.06379, -16.566906, -0.55819035, 24.06379, -8.610281, -0.28371692, 24.09806, -10.599218, -0.19208038, 24.089493, -8.610281, -0.28371692, 24.09806, -16.566906, -0.55819035, 24.06379, -8.610281, -0.55819035, 24.09806, -17.537725, -0.19208044, 25.020779, -16.566906, -0.19208038, 24.06379, -16.573536, -0.1920805, 25.050386, -10.599218, -0.19208038, 24.089493, -16.573536, -0.1920805, 25.050386, -16.566906, -0.19208038, 24.06379, -10.599218, -0.19208038, 24.089493, -8.61586, -0.19208044, 24.928009, -16.573536, -0.1920805, 25.050386, -8.61586, -0.19208044, 24.928009, -10.599218, -0.19208038, 24.089493, -8.611677, -0.19208038, 24.305765, -8.611677, -0.19208038, 24.305765, -10.599218, -0.19208038, 24.089493, -8.610281, -0.28371692, 24.09806, -16.566906, -0.19208038, 24.06379, -17.537725, -0.19208044, 25.020779, -17.02022, -0.19208038, 24.506718, -17.02022, -0.19208038, 24.506718, -17.537725, -0.19208044, 25.020779, -17.535063, -0.19208044, 24.624569, -17.535063, -0.19208044, 24.624569, -17.446188, -0.33171707, 24.51651, -17.02022, -0.19208038, 24.506718, -17.446188, -0.33171707, 24.51651, -17.533224, -0.19208038, 24.350975, -17.02022, -0.19208038, 24.506718, -16.566906, -0.19208038, 24.06379, -17.02022, -0.19208038, 24.506718, -17.533224, -0.19208038, 24.350975, -16.566906, -0.19208038, 24.06379, -17.533224, -0.19208038, 24.350975, -17.53112, -0.19208038, 24.038109, -16.573536, -0.55819046, 25.050386, -17.537725, -0.19208044, 25.020779, -16.573536, -0.1920805, 25.050386, -16.573536, -0.55819046, 25.050386, -17.537725, -0.55819046, 25.020779, -17.537725, -0.19208044, 25.020779, -8.61586, -0.19208044, 24.928009, -16.573536, -0.55819046, 25.050386, -16.573536, -0.1920805, 25.050386, -8.61586, -0.19208044, 24.928009, -8.61586, -0.5581904, 24.928009, -16.573536, -0.55819046, 25.050386, -17.53112, -0.55819035, 24.038109, -16.573536, -0.55819046, 25.050386, -16.566906, -0.55819035, 24.06379, -16.573536, -0.55819046, 25.050386, -17.53112, -0.55819035, 24.038109, -17.534693, -0.55819035, 24.569588, -16.573536, -0.55819046, 25.050386, -17.534693, -0.55819035, 24.569588, -17.537725, -0.55819046, 25.020779, -8.61586, -0.5581904, 24.928009, -16.566906, -0.55819035, 24.06379, -16.573536, -0.55819046, 25.050386, -8.61586, -0.5581904, 24.928009, -8.610281, -0.55819035, 24.09806, -16.566906, -0.55819035, 24.06379, -17.533224, -0.19208038, 24.350975, -17.446188, -0.33171707, 24.51651, -17.534693, -0.55819035, 24.569588, -17.446188, -0.33171707, 24.51651, -17.535063, -0.19208044, 24.624569, -17.534693, -0.55819035, 24.569588, -17.53112, -0.55819035, 24.038109, -17.533224, -0.19208038, 24.350975, -17.534693, -0.55819035, 24.569588, -17.53112, -0.55819035, 24.038109, -17.53112, -0.19208038, 24.038109, -17.533224, -0.19208038, 24.350975, -17.535063, -0.19208044, 24.624569, -17.537725, -0.55819046, 25.020779, -17.534693, -0.55819035, 24.569588, -17.535063, -0.19208044, 24.624569, -17.537725, -0.19208044, 25.020779, -17.537725, -0.55819046, 25.020779, -8.61586, -0.19208044, 24.928009, -8.610281, -0.55819035, 24.09806, -8.61586, -0.5581904, 24.928009, -8.610281, -0.55819035, 24.09806, -8.61586, -0.19208044, 24.928009, -8.611677, -0.19208038, 24.305765, -8.610281, -0.55819035, 24.09806, -8.611677, -0.19208038, 24.305765, -8.610281, -0.28371692, 24.09806, -10.358263, -0.5581907, 27.454252, -17.457956, -0.1920808, 27.566252, -10.358263, -0.19208074, 27.454252, -10.358263, -0.5581907, 27.454252, -17.457956, -0.5581907, 27.566252, -17.457956, -0.1920808, 27.566252, -8.72983, -0.19208074, 27.471743, -10.358263, -0.5581907, 27.454252, -10.358263, -0.19208074, 27.454252, -8.72983, -0.19208074, 27.471743, -8.72983, -0.5581907, 27.471743, -10.358263, -0.5581907, 27.454252, -10.351484, -0.5581906, 26.445835, -17.457956, -0.5581907, 27.566252, -10.358263, -0.5581907, 27.454252, -10.351484, -0.5581906, 26.445835, -17.451353, -0.5581906, 26.58358, -17.457956, -0.5581907, 27.566252, -8.72983, -0.5581907, 27.471743, -10.351484, -0.5581906, 26.445835, -10.358263, -0.5581907, 27.454252, -8.72983, -0.5581907, 27.471743, -8.724255, -0.5581906, 26.642231, -10.351484, -0.5581906, 26.445835, -8.72983, -0.5581907, 27.471743, -8.724255, -0.19208068, 26.642231, -8.724255, -0.5581906, 26.642231, -8.72983, -0.5581907, 27.471743, -8.72983, -0.19208074, 27.471743, -8.724255, -0.19208068, 26.642231, -10.35318, -0.19208068, 26.69805, -10.351484, -0.28371716, 26.445835, -9.944677, -0.19208062, 26.494825, -10.35318, -0.19208068, 26.69805, -12.1264515, -0.19208062, 26.480164, -10.351484, -0.28371716, 26.445835, -10.35318, -0.19208068, 26.69805, -16.120235, -0.19208068, 26.557617, -12.1264515, -0.19208062, 26.480164, -9.944677, -0.19208062, 26.494825, -10.358263, -0.19208074, 27.454252, -10.35318, -0.19208068, 26.69805, -9.944677, -0.19208062, 26.494825, -8.72983, -0.19208074, 27.471743, -10.358263, -0.19208074, 27.454252, -8.72983, -0.19208074, 27.471743, -9.944677, -0.19208062, 26.494825, -8.724255, -0.19208068, 26.642231, -17.457956, -0.1920808, 27.566252, -10.35318, -0.19208068, 26.69805, -10.358263, -0.19208074, 27.454252, -10.35318, -0.19208068, 26.69805, -17.457956, -0.1920808, 27.566252, -16.120235, -0.19208068, 26.557617, -16.120235, -0.19208068, 26.557617, -17.457956, -0.1920808, 27.566252, -17.453001, -0.19208068, 26.828812, -16.120235, -0.19208068, 26.557617, -17.453001, -0.19208068, 26.828812, -17.451353, -0.28371722, 26.58358, -8.724255, -0.19208068, 26.642231, -10.351484, -0.5581906, 26.445835, -8.724255, -0.5581906, 26.642231, -10.351484, -0.5581906, 26.445835, -8.724255, -0.19208068, 26.642231, -9.944677, -0.19208062, 26.494825, -10.351484, -0.5581906, 26.445835, -9.944677, -0.19208062, 26.494825, -10.351484, -0.28371716, 26.445835, -10.351484, -0.28371716, 26.445835, -17.451353, -0.5581906, 26.58358, -10.351484, -0.5581906, 26.445835, -10.351484, -0.28371716, 26.445835, -16.120235, -0.19208068, 26.557617, -17.451353, -0.5581906, 26.58358, -17.451353, -0.5581906, 26.58358, -16.120235, -0.19208068, 26.557617, -17.451353, -0.28371722, 26.58358, -16.120235, -0.19208068, 26.557617, -10.351484, -0.28371716, 26.445835, -12.1264515, -0.19208062, 26.480164, -17.451353, -0.28371722, 26.58358, -17.457956, -0.5581907, 27.566252, -17.451353, -0.5581906, 26.58358, -17.457956, -0.5581907, 27.566252, -17.451353, -0.28371722, 26.58358, -17.453001, -0.19208068, 26.828812, -17.457956, -0.5581907, 27.566252, -17.453001, -0.19208068, 26.828812, -17.457956, -0.1920808, 27.566252, -13.511387, 0.0055903792, 44.578655, -17.616987, 0.37126416, 44.736523, -13.511387, 0.37126416, 44.578655, -13.511387, 0.0055903792, 44.578655, -17.616987, 0.0055903792, 44.736523, -17.616987, 0.37126416, 44.736523, -9.277809, 0.37126416, 44.792564, -13.511387, 0.0055903792, 44.578655, -13.511387, 0.37126416, 44.578655, -9.277809, 0.37126416, 44.792564, -9.277809, 0.0055903792, 44.792564, -13.511387, 0.0055903792, 44.578655, -13.50481, 0.0055904984, 43.59991, -17.616987, 0.0055903792, 44.736523, -13.511387, 0.0055903792, 44.578655, -13.50481, 0.0055904984, 43.59991, -17.61141, 0.0055904984, 43.906574, -17.616987, 0.0055903792, 44.736523, -13.511387, 0.0055903792, 44.578655, -9.270195, 0.0055904984, 43.65935, -13.50481, 0.0055904984, 43.59991, -13.511387, 0.0055903792, 44.578655, -9.274065, 0.005590439, 44.23534, -9.270195, 0.0055904984, 43.65935, -9.274065, 0.005590439, 44.23534, -13.511387, 0.0055903792, 44.578655, -9.277809, 0.0055903792, 44.792564, -17.61141, 0.0055904984, 43.906574, -17.616987, 0.37126416, 44.736523, -17.616987, 0.0055903792, 44.736523, -17.61141, 0.0055904984, 43.906574, -17.61141, 0.37126416, 43.906574, -17.616987, 0.37126416, 44.736523, -9.274517, 0.37126416, 44.30254, -9.433565, 0.26217288, 44.20372, -9.274065, 0.005590439, 44.23534, -13.506453, 0.37126416, 43.84427, -13.50481, 0.28006393, 43.59991, -12.446262, 0.37126428, 43.61444, -13.506453, 0.37126416, 43.84427, -14.531677, 0.37126428, 43.676357, -13.50481, 0.28006393, 43.59991, -13.506453, 0.37126416, 43.84427, -17.61141, 0.37126416, 43.906574, -14.531677, 0.37126428, 43.676357, -13.506453, 0.37126416, 43.84427, -17.616987, 0.37126416, 44.736523, -17.61141, 0.37126416, 43.906574, -17.616987, 0.37126416, 44.736523, -13.506453, 0.37126416, 43.84427, -13.511387, 0.37126416, 44.578655, -12.446262, 0.37126428, 43.61444, -13.511387, 0.37126416, 44.578655, -13.506453, 0.37126416, 43.84427, -13.511387, 0.37126416, 44.578655, -9.274517, 0.37126416, 44.30254, -9.277809, 0.37126416, 44.792564, -13.511387, 0.37126416, 44.578655, -10.001682, 0.37126416, 44.19729, -9.274517, 0.37126416, 44.30254, -12.446262, 0.37126428, 43.61444, -10.001682, 0.37126416, 44.19729, -13.511387, 0.37126416, 44.578655, -9.433565, 0.26217288, 44.20372, -9.274517, 0.37126416, 44.30254, -10.001682, 0.37126416, 44.19729, -9.272657, 0.37126416, 44.02589, -9.433565, 0.26217288, 44.20372, -10.001682, 0.37126416, 44.19729, -10.001682, 0.37126416, 44.19729, -12.446262, 0.37126428, 43.61444, -9.272657, 0.37126416, 44.02589, -9.272657, 0.37126416, 44.02589, -12.446262, 0.37126428, 43.61444, -9.270195, 0.37126428, 43.65935, -9.277809, 0.0055903792, 44.792564, -9.274517, 0.37126416, 44.30254, -9.274065, 0.005590439, 44.23534, -9.277809, 0.0055903792, 44.792564, -9.277809, 0.37126416, 44.792564, -9.274517, 0.37126416, 44.30254, -9.272657, 0.37126416, 44.02589, -9.270195, 0.0055904984, 43.65935, -9.274065, 0.005590439, 44.23534, -9.272657, 0.37126416, 44.02589, -9.270195, 0.37126428, 43.65935, -9.270195, 0.0055904984, 43.65935, -9.433565, 0.26217288, 44.20372, -9.272657, 0.37126416, 44.02589, -9.274065, 0.005590439, 44.23534, -9.270195, 0.37126428, 43.65935, -13.50481, 0.0055904984, 43.59991, -9.270195, 0.0055904984, 43.65935, -13.50481, 0.0055904984, 43.59991, -9.270195, 0.37126428, 43.65935, -12.446262, 0.37126428, 43.61444, -13.50481, 0.0055904984, 43.59991, -12.446262, 0.37126428, 43.61444, -13.50481, 0.28006393, 43.59991, -13.50481, 0.28006393, 43.59991, -17.61141, 0.0055904984, 43.906574, -13.50481, 0.0055904984, 43.59991, -13.50481, 0.28006393, 43.59991, -17.61141, 0.37126416, 43.906574, -17.61141, 0.0055904984, 43.906574, -17.61141, 0.37126416, 43.906574, -13.50481, 0.28006393, 43.59991, -14.531677, 0.37126428, 43.676357, -16.691502, 0.37126452, 41.174942, -17.592882, 0.0055907965, 41.149685, -16.691502, 0.0055907965, 41.174942, -16.691502, 0.37126452, 41.174942, -17.592882, 0.37126452, 41.149685, -17.592882, 0.0055907965, 41.149685, -16.691502, 0.0055907965, 41.174942, -11.113045, 0.37126452, 41.198032, -16.691502, 0.37126452, 41.174942, -16.691502, 0.0055907965, 41.174942, -9.253704, 0.28006417, 41.20573, -11.113045, 0.37126452, 41.198032, -9.253704, 0.28006417, 41.20573, -16.691502, 0.0055907965, 41.174942, -9.253704, 0.0055907965, 41.20573, -17.599485, 0.3712644, 42.132355, -16.691502, 0.37126452, 41.174942, -16.698133, 0.3712644, 42.16154, -11.113045, 0.37126452, 41.198032, -16.698133, 0.3712644, 42.16154, -16.691502, 0.37126452, 41.174942, -11.113045, 0.37126452, 41.198032, -9.259279, 0.3712644, 42.03524, -16.698133, 0.3712644, 42.16154, -9.259279, 0.3712644, 42.03524, -11.113045, 0.37126452, 41.198032, -9.255098, 0.37126452, 41.413, -9.255098, 0.37126452, 41.413, -11.113045, 0.37126452, 41.198032, -9.253704, 0.28006417, 41.20573, -16.691502, 0.37126452, 41.174942, -17.599485, 0.3712644, 42.132355, -17.115143, 0.37126452, 41.617634, -17.115143, 0.37126452, 41.617634, -17.599485, 0.3712644, 42.132355, -17.596823, 0.37126452, 41.736145, -17.596823, 0.37126452, 41.736145, -17.513622, 0.23162776, 41.628048, -17.115143, 0.37126452, 41.617634, -17.513622, 0.23162776, 41.628048, -17.594984, 0.37126452, 41.46255, -17.115143, 0.37126452, 41.617634, -16.691502, 0.37126452, 41.174942, -17.115143, 0.37126452, 41.617634, -17.594984, 0.37126452, 41.46255, -16.691502, 0.37126452, 41.174942, -17.594984, 0.37126452, 41.46255, -17.592882, 0.37126452, 41.149685, -16.698133, 0.0055906773, 42.16154, -17.599485, 0.3712644, 42.132355, -16.698133, 0.3712644, 42.16154, -16.698133, 0.0055906773, 42.16154, -17.599485, 0.0055906773, 42.132355, -17.599485, 0.3712644, 42.132355, -9.259279, 0.3712644, 42.03524, -16.698133, 0.0055906773, 42.16154, -16.698133, 0.3712644, 42.16154, -9.259279, 0.3712644, 42.03524, -9.259279, 0.005590737, 42.03524, -16.698133, 0.0055906773, 42.16154, -17.592882, 0.0055907965, 41.149685, -16.698133, 0.0055906773, 42.16154, -16.691502, 0.0055907965, 41.174942, -16.698133, 0.0055906773, 42.16154, -17.592882, 0.0055907965, 41.149685, -17.59645, 0.005590737, 41.68073, -16.698133, 0.0055906773, 42.16154, -17.59645, 0.005590737, 41.68073, -17.599485, 0.0055906773, 42.132355, -9.259279, 0.005590737, 42.03524, -16.691502, 0.0055907965, 41.174942, -16.698133, 0.0055906773, 42.16154, -9.259279, 0.005590737, 42.03524, -9.253704, 0.0055907965, 41.20573, -16.691502, 0.0055907965, 41.174942, -17.594984, 0.37126452, 41.46255, -17.513622, 0.23162776, 41.628048, -17.59645, 0.005590737, 41.68073, -17.513622, 0.23162776, 41.628048, -17.596823, 0.37126452, 41.736145, -17.59645, 0.005590737, 41.68073, -17.592882, 0.0055907965, 41.149685, -17.594984, 0.37126452, 41.46255, -17.59645, 0.005590737, 41.68073, -17.592882, 0.0055907965, 41.149685, -17.592882, 0.37126452, 41.149685, -17.594984, 0.37126452, 41.46255, -17.596823, 0.37126452, 41.736145, -17.599485, 0.0055906773, 42.132355, -17.59645, 0.005590737, 41.68073, -17.596823, 0.37126452, 41.736145, -17.599485, 0.3712644, 42.132355, -17.599485, 0.0055906773, 42.132355, -9.259279, 0.3712644, 42.03524, -9.253704, 0.0055907965, 41.20573, -9.259279, 0.005590737, 42.03524, -9.253704, 0.0055907965, 41.20573, -9.259279, 0.3712644, 42.03524, -9.255098, 0.37126452, 41.413, -9.253704, 0.0055907965, 41.20573, -9.255098, 0.37126452, 41.413, -9.253704, 0.28006417, 41.20573, -9.466791, -0.19077104, 22.980423, -9.698496, -0.09826177, 22.978865, -9.698496, -0.19077104, 22.978865, -9.466791, -0.19077104, 22.980423, -9.466791, -0.09826177, 22.980423, -9.698496, -0.09826177, 22.978865, -9.466791, -0.09826177, 22.980423, -9.864088, -0.098264754, 47.618515, -9.698496, -0.09826177, 22.978865, -9.466791, -0.09826177, 22.980423, -9.632383, -0.098264754, 47.62007, -9.864088, -0.098264754, 47.618515, -9.632383, -0.098264754, 47.62007, -9.864088, -0.19077402, 47.618515, -9.864088, -0.098264754, 47.618515, -9.632383, -0.098264754, 47.62007, -9.632383, -0.19077402, 47.62007, -9.864088, -0.19077402, 47.618515, -9.632383, -0.19077402, 47.62007, -9.698496, -0.19077104, 22.978865, -9.864088, -0.19077402, 47.618515, -9.632383, -0.19077402, 47.62007, -9.466791, -0.19077104, 22.980423, -9.698496, -0.19077104, 22.978865, -9.698496, -0.19077104, 22.978865, -9.864088, -0.098264754, 47.618515, -9.864088, -0.19077402, 47.618515, -9.698496, -0.19077104, 22.978865, -9.698496, -0.09826177, 22.978865, -9.864088, -0.098264754, 47.618515, -9.632383, -0.19077402, 47.62007, -9.466791, -0.09826177, 22.980423, -9.466791, -0.19077104, 22.980423, -9.632383, -0.19077402, 47.62007, -9.632383, -0.098264754, 47.62007, -9.466791, -0.09826177, 22.980423, -10.65929, 0.00559026, 45.949276, -17.916506, 0.37126392, 46.05978, -10.65929, 0.37126392, 45.949276, -10.65929, 0.00559026, 45.949276, -17.916506, 0.0055902004, 46.05978, -17.916506, 0.37126392, 46.05978, -8.99464, 0.37126392, 45.967007, -10.65929, 0.00559026, 45.949276, -10.65929, 0.37126392, 45.949276, -8.99464, 0.37126392, 45.967007, -8.99464, 0.00559026, 45.967007, -10.65929, 0.00559026, 45.949276, -10.65251, 0.0055903792, 44.940422, -17.916506, 0.0055902004, 46.05978, -10.65929, 0.00559026, 45.949276, -10.65251, 0.0055903792, 44.940422, -17.909903, 0.0055903196, 45.07711, -17.916506, 0.0055902004, 46.05978, -8.99464, 0.00559026, 45.967007, -10.65251, 0.0055903792, 44.940422, -10.65929, 0.00559026, 45.949276, -8.99464, 0.00559026, 45.967007, -8.989063, 0.0055903196, 45.137062, -10.65251, 0.0055903792, 44.940422, -8.99464, 0.00559026, 45.967007, -8.989063, 0.37126404, 45.137062, -8.989063, 0.0055903196, 45.137062, -8.99464, 0.00559026, 45.967007, -8.99464, 0.37126392, 45.967007, -8.989063, 0.37126404, 45.137062, -10.654205, 0.37126404, 45.192635, -10.65251, 0.2800637, 44.940422, -10.236538, 0.37126404, 44.98947, -10.654205, 0.37126404, 45.192635, -12.466749, 0.37126404, 44.974487, -10.65251, 0.2800637, 44.940422, -10.654205, 0.37126404, 45.192635, -16.549116, 0.37126404, 45.05178, -12.466749, 0.37126404, 44.974487, -10.236538, 0.37126404, 44.98947, -10.65929, 0.37126392, 45.949276, -10.654205, 0.37126404, 45.192635, -10.236538, 0.37126404, 44.98947, -8.99464, 0.37126392, 45.967007, -10.65929, 0.37126392, 45.949276, -8.99464, 0.37126392, 45.967007, -10.236538, 0.37126404, 44.98947, -8.989063, 0.37126404, 45.137062, -17.916506, 0.37126392, 46.05978, -10.654205, 0.37126404, 45.192635, -10.65929, 0.37126392, 45.949276, -10.654205, 0.37126404, 45.192635, -17.916506, 0.37126392, 46.05978, -16.549116, 0.37126404, 45.05178, -16.549116, 0.37126404, 45.05178, -17.916506, 0.37126392, 46.05978, -17.911556, 0.37126404, 45.32321, -16.549116, 0.37126404, 45.05178, -17.911556, 0.37126404, 45.32321, -17.909903, 0.2800637, 45.07711, -8.989063, 0.37126404, 45.137062, -10.65251, 0.0055903792, 44.940422, -8.989063, 0.0055903196, 45.137062, -10.65251, 0.0055903792, 44.940422, -8.989063, 0.37126404, 45.137062, -10.236538, 0.37126404, 44.98947, -10.65251, 0.0055903792, 44.940422, -10.236538, 0.37126404, 44.98947, -10.65251, 0.2800637, 44.940422, -10.65251, 0.2800637, 44.940422, -17.909903, 0.0055903196, 45.07711, -10.65251, 0.0055903792, 44.940422, -10.65251, 0.2800637, 44.940422, -16.549116, 0.37126404, 45.05178, -17.909903, 0.0055903196, 45.07711, -17.909903, 0.0055903196, 45.07711, -16.549116, 0.37126404, 45.05178, -17.909903, 0.2800637, 45.07711, -16.549116, 0.37126404, 45.05178, -10.65251, 0.2800637, 44.940422, -12.466749, 0.37126404, 44.974487, -17.909903, 0.2800637, 45.07711, -17.916506, 0.0055902004, 46.05978, -17.909903, 0.0055903196, 45.07711, -17.916506, 0.0055902004, 46.05978, -17.909903, 0.2800637, 45.07711, -17.911556, 0.37126404, 45.32321, -17.916506, 0.0055902004, 46.05978, -17.911556, 0.37126404, 45.32321, -17.916506, 0.37126392, 46.05978, -9.086136, -3.458273, 47.63378, -9.862558, 1.270175, 47.455757, -9.690924, -3.458273, 47.629715, -9.086136, -3.458273, 47.63378, -8.912178, 1.270175, 47.462143, -9.862558, 1.270175, 47.455757, -8.912178, 1.270175, 47.462143, -9.868942, 1.2701747, 48.4057, -9.862558, 1.270175, 47.455757, -8.912178, 1.270175, 47.462143, -8.918562, 1.2701747, 48.41209, -9.868942, 1.2701747, 48.4057, -8.918562, 1.2701747, 48.41209, -9.694984, -3.458273, 48.234066, -9.868942, 1.2701747, 48.4057, -8.918562, 1.2701747, 48.41209, -9.090197, -3.458273, 48.238132, -9.694984, -3.458273, 48.234066, -9.090197, -3.458273, 48.238132, -9.690924, -3.458273, 47.629715, -9.694984, -3.458273, 48.234066, -9.090197, -3.458273, 48.238132, -9.086136, -3.458273, 47.63378, -9.690924, -3.458273, 47.629715, -9.690924, -3.458273, 47.629715, -9.868942, 1.2701747, 48.4057, -9.694984, -3.458273, 48.234066, -9.690924, -3.458273, 47.629715, -9.862558, 1.270175, 47.455757, -9.868942, 1.2701747, 48.4057, -9.090197, -3.458273, 48.238132, -8.912178, 1.270175, 47.462143, -9.086136, -3.458273, 47.63378, -9.090197, -3.458273, 48.238132, -8.918562, 1.2701747, 48.41209, -8.912178, 1.270175, 47.462143, -16.72666, 0.37126392, 46.406403, -17.628038, 0.0055902004, 46.380707, -16.72666, 0.0055902004, 46.406403, -16.72666, 0.37126392, 46.406403, -17.628038, 0.37126392, 46.380707, -17.628038, 0.0055902004, 46.380707, -16.72666, 0.0055902004, 46.406403, -11.148199, 0.37126392, 46.429054, -16.72666, 0.37126392, 46.406403, -16.72666, 0.0055902004, 46.406403, -9.28886, 0.28006357, 46.436752, -11.148199, 0.37126392, 46.429054, -9.28886, 0.28006357, 46.436752, -16.72666, 0.0055902004, 46.406403, -9.28886, 0.0055902004, 46.436752, -17.634645, 0.3712638, 47.363815, -16.72666, 0.37126392, 46.406403, -16.733288, 0.3712638, 47.392563, -11.148199, 0.37126392, 46.429054, -16.733288, 0.3712638, 47.392563, -16.72666, 0.37126392, 46.406403, -11.148199, 0.37126392, 46.429054, -9.294437, 0.3712638, 47.266697, -16.733288, 0.3712638, 47.392563, -9.294437, 0.3712638, 47.266697, -11.148199, 0.37126392, 46.429054, -9.2902565, 0.37126392, 46.644455, -9.2902565, 0.37126392, 46.644455, -11.148199, 0.37126392, 46.429054, -9.28886, 0.28006357, 46.436752, -16.72666, 0.37126392, 46.406403, -17.634645, 0.3712638, 47.363815, -17.150305, 0.3712638, 46.84953, -17.150305, 0.3712638, 46.84953, -17.634645, 0.3712638, 47.363815, -17.631985, 0.3712638, 46.96804, -17.631985, 0.3712638, 46.96804, -17.548779, 0.23162705, 46.85907, -17.150305, 0.3712638, 46.84953, -17.548779, 0.23162705, 46.85907, -17.63014, 0.37126392, 46.693573, -17.150305, 0.3712638, 46.84953, -16.72666, 0.37126392, 46.406403, -17.150305, 0.3712638, 46.84953, -17.63014, 0.37126392, 46.693573, -16.72666, 0.37126392, 46.406403, -17.63014, 0.37126392, 46.693573, -17.628038, 0.37126392, 46.380707, -16.733288, 0.005590081, 47.392563, -17.634645, 0.3712638, 47.363815, -16.733288, 0.3712638, 47.392563, -16.733288, 0.005590081, 47.392563, -17.634645, 0.005590081, 47.363815, -17.634645, 0.3712638, 47.363815, -9.294437, 0.3712638, 47.266697, -16.733288, 0.005590081, 47.392563, -16.733288, 0.3712638, 47.392563, -9.294437, 0.3712638, 47.266697, -9.294437, 0.005590081, 47.266697, -16.733288, 0.005590081, 47.392563, -17.628038, 0.0055902004, 46.380707, -16.733288, 0.005590081, 47.392563, -16.72666, 0.0055902004, 46.406403, -16.733288, 0.005590081, 47.392563, -17.628038, 0.0055902004, 46.380707, -17.631613, 0.005590141, 46.912624, -16.733288, 0.005590081, 47.392563, -17.631613, 0.005590141, 46.912624, -17.634645, 0.005590081, 47.363815, -9.294437, 0.005590081, 47.266697, -16.72666, 0.0055902004, 46.406403, -16.733288, 0.005590081, 47.392563, -9.294437, 0.005590081, 47.266697, -9.28886, 0.0055902004, 46.436752, -16.72666, 0.0055902004, 46.406403, -17.63014, 0.37126392, 46.693573, -17.548779, 0.23162705, 46.85907, -17.631613, 0.005590141, 46.912624, -17.548779, 0.23162705, 46.85907, -17.631985, 0.3712638, 46.96804, -17.631613, 0.005590141, 46.912624, -17.628038, 0.0055902004, 46.380707, -17.63014, 0.37126392, 46.693573, -17.631613, 0.005590141, 46.912624, -17.628038, 0.0055902004, 46.380707, -17.628038, 0.37126392, 46.380707, -17.63014, 0.37126392, 46.693573, -17.631985, 0.3712638, 46.96804, -17.634645, 0.005590081, 47.363815, -17.631613, 0.005590141, 46.912624, -17.631985, 0.3712638, 46.96804, -17.634645, 0.3712638, 47.363815, -17.634645, 0.005590081, 47.363815, -9.294437, 0.3712638, 47.266697, -9.28886, 0.0055902004, 46.436752, -9.294437, 0.005590081, 47.266697, -9.28886, 0.0055902004, 46.436752, -9.294437, 0.3712638, 47.266697, -9.2902565, 0.37126392, 46.644455, -9.28886, 0.0055902004, 46.436752, -9.2902565, 0.37126392, 46.644455, -9.28886, 0.28006357, 46.436752, -16.91135, 0.37126464, 39.993507, -17.875563, 0.0055909753, 39.967392, -16.91135, 0.0055909753, 39.993507, -16.91135, 0.37126464, 39.993507, -17.875563, 0.37126464, 39.967392, -17.875563, 0.0055909753, 39.967392, -16.91135, 0.0055909753, 39.993507, -10.943661, 0.37126464, 40.01878, -16.91135, 0.37126464, 39.993507, -16.91135, 0.0055909753, 39.993507, -8.954723, 0.28006428, 40.027344, -10.943661, 0.37126464, 40.01878, -8.954723, 0.28006428, 40.027344, -16.91135, 0.0055909753, 39.993507, -8.954723, 0.0055909753, 40.027344, -17.882166, 0.37126452, 40.95006, -16.91135, 0.37126464, 39.993507, -16.917978, 0.37126452, 40.97967, -10.943661, 0.37126464, 40.01878, -16.917978, 0.37126452, 40.97967, -16.91135, 0.37126464, 39.993507, -10.943661, 0.37126464, 40.01878, -8.9603, 0.37126452, 40.857292, -16.917978, 0.37126452, 40.97967, -8.9603, 0.37126452, 40.857292, -10.943661, 0.37126464, 40.01878, -8.95612, 0.37126464, 40.23505, -8.95612, 0.37126464, 40.23505, -10.943661, 0.37126464, 40.01878, -8.954723, 0.28006428, 40.027344, -16.91135, 0.37126464, 39.993507, -17.882166, 0.37126452, 40.95006, -17.364662, 0.37126464, 40.436, -17.364662, 0.37126464, 40.436, -17.882166, 0.37126452, 40.95006, -17.879507, 0.37126464, 40.55429, -17.879507, 0.37126464, 40.55429, -17.79063, 0.23162788, 40.445793, -17.364662, 0.37126464, 40.436, -17.79063, 0.23162788, 40.445793, -17.877665, 0.37126464, 40.28026, -17.364662, 0.37126464, 40.436, -16.91135, 0.37126464, 39.993507, -17.364662, 0.37126464, 40.436, -17.877665, 0.37126464, 40.28026, -16.91135, 0.37126464, 39.993507, -17.877665, 0.37126464, 40.28026, -17.875563, 0.37126464, 39.967392, -16.917978, 0.005590856, 40.97967, -17.882166, 0.37126452, 40.95006, -16.917978, 0.37126452, 40.97967, -16.917978, 0.005590856, 40.97967, -17.882166, 0.005590856, 40.95006, -17.882166, 0.37126452, 40.95006, -8.9603, 0.37126452, 40.857292, -16.917978, 0.005590856, 40.97967, -16.917978, 0.37126452, 40.97967, -8.9603, 0.37126452, 40.857292, -8.9603, 0.005590856, 40.857292, -16.917978, 0.005590856, 40.97967, -17.875563, 0.0055909753, 39.967392, -16.917978, 0.005590856, 40.97967, -16.91135, 0.0055909753, 39.993507, -16.917978, 0.005590856, 40.97967, -17.875563, 0.0055909753, 39.967392, -17.879133, 0.0055909157, 40.49887, -16.917978, 0.005590856, 40.97967, -17.879133, 0.0055909157, 40.49887, -17.882166, 0.005590856, 40.95006, -8.9603, 0.005590856, 40.857292, -16.91135, 0.0055909753, 39.993507, -16.917978, 0.005590856, 40.97967, -8.9603, 0.005590856, 40.857292, -8.954723, 0.0055909753, 40.027344, -16.91135, 0.0055909753, 39.993507, -17.877665, 0.37126464, 40.28026, -17.79063, 0.23162788, 40.445793, -17.879133, 0.0055909157, 40.49887, -17.79063, 0.23162788, 40.445793, -17.879507, 0.37126464, 40.55429, -17.879133, 0.0055909157, 40.49887, -17.875563, 0.0055909753, 39.967392, -17.877665, 0.37126464, 40.28026, -17.879133, 0.0055909157, 40.49887, -17.875563, 0.0055909753, 39.967392, -17.875563, 0.37126464, 39.967392, -17.877665, 0.37126464, 40.28026, -17.879507, 0.37126464, 40.55429, -17.882166, 0.005590856, 40.95006, -17.879133, 0.0055909157, 40.49887, -17.879507, 0.37126464, 40.55429, -17.882166, 0.37126452, 40.95006, -17.882166, 0.005590856, 40.95006, -8.9603, 0.37126452, 40.857292, -8.954723, 0.0055909753, 40.027344, -8.9603, 0.005590856, 40.857292, -8.954723, 0.0055909753, 40.027344, -8.9603, 0.37126452, 40.857292, -8.95612, 0.37126464, 40.23505, -8.954723, 0.0055909753, 40.027344, -8.95612, 0.37126464, 40.23505, -8.954723, 0.28006428, 40.027344, -13.4819145, 0.005591035, 39.478943, -17.873325, 0.37126476, 39.634453, -13.4819145, 0.37126476, 39.478943, -13.4819145, 0.005591035, 39.478943, -17.873325, 0.0055909753, 39.634453, -17.873325, 0.37126476, 39.634453, -8.952486, 0.37126476, 39.694405, -13.4819145, 0.005591035, 39.478943, -13.4819145, 0.37126476, 39.478943, -8.952486, 0.37126476, 39.694405, -8.952486, 0.0055909753, 39.694405, -13.4819145, 0.005591035, 39.478943, -13.47533, 0.005591154, 38.49933, -17.873325, 0.0055909753, 39.634453, -13.4819145, 0.005591035, 39.478943, -13.47533, 0.005591154, 38.49933, -17.867746, 0.0055910945, 38.80451, -17.873325, 0.0055909753, 39.634453, -13.4819145, 0.005591035, 39.478943, -8.944866, 0.005591154, 38.560757, -13.47533, 0.005591154, 38.49933, -13.4819145, 0.005591035, 39.478943, -8.948738, 0.005591035, 39.136745, -8.944866, 0.005591154, 38.560757, -8.948738, 0.005591035, 39.136745, -13.4819145, 0.005591035, 39.478943, -8.952486, 0.0055909753, 39.694405, -17.867746, 0.0055910945, 38.80451, -17.873325, 0.37126476, 39.634453, -17.873325, 0.0055909753, 39.634453, -17.867746, 0.0055910945, 38.80451, -17.867746, 0.37126476, 38.80451, -17.873325, 0.37126476, 39.634453, -8.949188, 0.37126476, 39.203945, -9.119587, 0.26217347, 39.105488, -8.948738, 0.005591035, 39.136745, -13.476976, 0.37126487, 38.74412, -13.47533, 0.28006452, 38.49933, -12.3426075, 0.37126487, 38.514793, -13.476976, 0.37126487, 38.74412, -14.573328, 0.37126487, 38.575733, -13.47533, 0.28006452, 38.49933, -13.476976, 0.37126487, 38.74412, -17.867746, 0.37126476, 38.80451, -14.573328, 0.37126487, 38.575733, -13.476976, 0.37126487, 38.74412, -17.873325, 0.37126476, 39.634453, -17.867746, 0.37126476, 38.80451, -17.873325, 0.37126476, 39.634453, -13.476976, 0.37126487, 38.74412, -13.4819145, 0.37126476, 39.478943, -12.3426075, 0.37126487, 38.514793, -13.4819145, 0.37126476, 39.478943, -13.476976, 0.37126487, 38.74412, -13.4819145, 0.37126476, 39.478943, -8.949188, 0.37126476, 39.203945, -8.952486, 0.37126476, 39.694405, -13.4819145, 0.37126476, 39.478943, -9.726973, 0.37126476, 39.09835, -8.949188, 0.37126476, 39.203945, -12.3426075, 0.37126487, 38.514793, -9.726973, 0.37126476, 39.09835, -13.4819145, 0.37126476, 39.478943, -9.119587, 0.26217347, 39.105488, -8.949188, 0.37126476, 39.203945, -9.726973, 0.37126476, 39.09835, -8.94733, 0.37126476, 38.927296, -9.119587, 0.26217347, 39.105488, -9.726973, 0.37126476, 39.09835, -9.726973, 0.37126476, 39.09835, -12.3426075, 0.37126487, 38.514793, -8.94733, 0.37126476, 38.927296, -8.94733, 0.37126476, 38.927296, -12.3426075, 0.37126487, 38.514793, -8.944866, 0.37126487, 38.560757, -8.952486, 0.0055909753, 39.694405, -8.949188, 0.37126476, 39.203945, -8.948738, 0.005591035, 39.136745, -8.952486, 0.0055909753, 39.694405, -8.952486, 0.37126476, 39.694405, -8.949188, 0.37126476, 39.203945, -8.94733, 0.37126476, 38.927296, -8.944866, 0.005591154, 38.560757, -8.948738, 0.005591035, 39.136745, -8.94733, 0.37126476, 38.927296, -8.944866, 0.37126487, 38.560757, -8.944866, 0.005591154, 38.560757, -9.119587, 0.26217347, 39.105488, -8.94733, 0.37126476, 38.927296, -8.948738, 0.005591035, 39.136745, -8.944866, 0.37126487, 38.560757, -13.47533, 0.005591154, 38.49933, -8.944866, 0.005591154, 38.560757, -13.47533, 0.005591154, 38.49933, -8.944866, 0.37126487, 38.560757, -12.3426075, 0.37126487, 38.514793, -13.47533, 0.005591154, 38.49933, -12.3426075, 0.37126487, 38.514793, -13.47533, 0.28006452, 38.49933, -13.47533, 0.28006452, 38.49933, -17.867746, 0.0055910945, 38.80451, -13.47533, 0.005591154, 38.49933, -13.47533, 0.28006452, 38.49933, -17.867746, 0.37126476, 38.80451, -17.867746, 0.0055910945, 38.80451, -17.867746, 0.37126476, 38.80451, -13.47533, 0.28006452, 38.49933, -14.573328, 0.37126487, 38.575733, -16.664772, 0.371265, 37.19757, -17.566149, 0.0055912733, 37.171875, -16.664772, 0.0055912733, 37.19757, -16.664772, 0.371265, 37.19757, -17.566149, 0.371265, 37.171875, -17.566149, 0.0055912733, 37.171875, -16.664772, 0.0055912733, 37.19757, -11.086311, 0.371265, 37.220222, -16.664772, 0.371265, 37.19757, -16.664772, 0.0055912733, 37.19757, -9.226971, 0.28006464, 37.227917, -11.086311, 0.371265, 37.220222, -9.226971, 0.28006464, 37.227917, -16.664772, 0.0055912733, 37.19757, -9.226971, 0.0055912733, 37.227917, -17.572756, 0.37126487, 38.154984, -16.664772, 0.371265, 37.19757, -16.6714, 0.37126487, 38.18373, -11.086311, 0.371265, 37.220222, -16.6714, 0.37126487, 38.18373, -16.664772, 0.371265, 37.19757, -11.086311, 0.371265, 37.220222, -9.23255, 0.37126487, 38.057865, -16.6714, 0.37126487, 38.18373, -9.23255, 0.37126487, 38.057865, -11.086311, 0.371265, 37.220222, -9.228367, 0.371265, 37.435623, -9.228367, 0.371265, 37.435623, -11.086311, 0.371265, 37.220222, -9.226971, 0.28006464, 37.227917, -16.664772, 0.371265, 37.19757, -17.572756, 0.37126487, 38.154984, -17.088415, 0.371265, 37.640697, -17.088415, 0.371265, 37.640697, -17.572756, 0.37126487, 38.154984, -17.570097, 0.371265, 37.75921, -17.570097, 0.371265, 37.75921, -17.486889, 0.23162824, 37.650238, -17.088415, 0.371265, 37.640697, -17.486889, 0.23162824, 37.650238, -17.56825, 0.371265, 37.48474, -17.088415, 0.371265, 37.640697, -16.664772, 0.371265, 37.19757, -17.088415, 0.371265, 37.640697, -17.56825, 0.371265, 37.48474, -16.664772, 0.371265, 37.19757, -17.56825, 0.371265, 37.48474, -17.566149, 0.371265, 37.171875, -16.6714, 0.005591154, 38.18373, -17.572756, 0.37126487, 38.154984, -16.6714, 0.37126487, 38.18373, -16.6714, 0.005591154, 38.18373, -17.572756, 0.005591154, 38.154984, -17.572756, 0.37126487, 38.154984, -9.23255, 0.37126487, 38.057865, -16.6714, 0.005591154, 38.18373, -16.6714, 0.37126487, 38.18373, -9.23255, 0.37126487, 38.057865, -9.23255, 0.0055912137, 38.057865, -16.6714, 0.005591154, 38.18373, -17.566149, 0.0055912733, 37.171875, -16.6714, 0.005591154, 38.18373, -16.664772, 0.0055912733, 37.19757, -16.6714, 0.005591154, 38.18373, -17.566149, 0.0055912733, 37.171875, -17.569721, 0.0055912137, 37.703354, -16.6714, 0.005591154, 38.18373, -17.569721, 0.0055912137, 37.703354, -17.572756, 0.005591154, 38.154984, -9.23255, 0.0055912137, 38.057865, -16.664772, 0.0055912733, 37.19757, -16.6714, 0.005591154, 38.18373, -9.23255, 0.0055912137, 38.057865, -9.226971, 0.0055912733, 37.227917, -16.664772, 0.0055912733, 37.19757, -17.56825, 0.371265, 37.48474, -17.486889, 0.23162824, 37.650238, -17.569721, 0.0055912137, 37.703354, -17.486889, 0.23162824, 37.650238, -17.570097, 0.371265, 37.75921, -17.569721, 0.0055912137, 37.703354, -17.566149, 0.0055912733, 37.171875, -17.56825, 0.371265, 37.48474, -17.569721, 0.0055912137, 37.703354, -17.566149, 0.0055912733, 37.171875, -17.566149, 0.371265, 37.171875, -17.56825, 0.371265, 37.48474, -17.570097, 0.371265, 37.75921, -17.572756, 0.005591154, 38.154984, -17.569721, 0.0055912137, 37.703354, -17.570097, 0.371265, 37.75921, -17.572756, 0.37126487, 38.154984, -17.572756, 0.005591154, 38.154984, -9.23255, 0.37126487, 38.057865, -9.226971, 0.0055912733, 37.227917, -9.23255, 0.0055912137, 38.057865, -9.226971, 0.0055912733, 37.227917, -9.23255, 0.37126487, 38.057865, -9.228367, 0.371265, 37.435623, -9.226971, 0.0055912733, 37.227917, -9.228367, 0.371265, 37.435623, -9.226971, 0.28006464, 37.227917, -16.810944, -3.458273, 47.519463, -17.46208, 1.270175, 47.59407, -17.54053, -3.458273, 47.51456, -16.810944, -3.458273, 47.519463, -16.890453, 1.270175, 47.59791, -17.46208, 1.270175, 47.59407, -16.890453, 1.270175, 47.59791, -17.465921, 1.270175, 48.165695, -17.46208, 1.270175, 47.59407, -16.890453, 1.270175, 47.59791, -16.894295, 1.270175, 48.16954, -17.465921, 1.270175, 48.165695, -16.894295, 1.270175, 48.16954, -17.545431, -3.458273, 48.24415, -17.465921, 1.270175, 48.165695, -16.894295, 1.270175, 48.16954, -16.815845, -3.458273, 48.24905, -17.545431, -3.458273, 48.24415, -16.815845, -3.458273, 48.24905, -17.54053, -3.458273, 47.51456, -17.545431, -3.458273, 48.24415, -16.815845, -3.458273, 48.24905, -16.810944, -3.458273, 47.519463, -17.54053, -3.458273, 47.51456, -17.54053, -3.458273, 47.51456, -17.465921, 1.270175, 48.165695, -17.545431, -3.458273, 48.24415, -17.54053, -3.458273, 47.51456, -17.46208, 1.270175, 47.59407, -17.465921, 1.270175, 48.165695, -16.815845, -3.458273, 48.24905, -16.890453, 1.270175, 47.59791, -16.810944, -3.458273, 47.519463, -16.815845, -3.458273, 48.24905, -16.894295, 1.270175, 48.16954, -16.890453, 1.270175, 47.59791, -9.055229, -3.45827, 22.581722, -9.785252, 1.2701778, 22.576817, -9.785252, -3.45827, 22.576817, -9.055229, -3.45827, 22.581722, -9.055229, 1.2701778, 22.581722, -9.785252, 1.2701778, 22.576817, -9.060133, -3.45827, 23.31131, -9.785252, -3.45827, 22.576817, -9.790155, -3.45827, 23.306404, -9.060133, -3.45827, 23.31131, -9.055229, -3.45827, 22.581722, -9.785252, -3.45827, 22.576817, -9.785252, -3.45827, 22.576817, -9.790155, 1.2701778, 23.306404, -9.790155, -3.45827, 23.306404, -9.785252, -3.45827, 22.576817, -9.785252, 1.2701778, 22.576817, -9.790155, 1.2701778, 23.306404, -9.060133, 0.08806592, 23.31131, -9.242529, 1.2701778, 23.310083, -9.058907, 1.2701778, 23.128912, -9.055229, -3.45827, 22.581722, -9.060133, 0.08806592, 23.31131, -9.058907, 1.2701778, 23.128912, -9.060133, 0.08806592, 23.31131, -9.055229, -3.45827, 22.581722, -9.060133, -3.45827, 23.31131, -9.055229, -3.45827, 22.581722, -9.058907, 1.2701778, 23.128912, -9.055229, 1.2701778, 22.581722, -9.058907, 1.2701778, 23.128912, -9.785252, 1.2701778, 22.576817, -9.055229, 1.2701778, 22.581722, -9.785252, 1.2701778, 22.576817, -9.058907, 1.2701778, 23.128912, -9.242529, 1.2701778, 23.310083, -9.785252, 1.2701778, 22.576817, -9.242529, 1.2701778, 23.310083, -9.790155, 1.2701778, 23.306404, -9.060133, -3.45827, 23.31131, -9.242529, 1.2701778, 23.310083, -9.060133, 0.08806592, 23.31131, -9.242529, 1.2701778, 23.310083, -9.060133, -3.45827, 23.31131, -9.790155, -3.45827, 23.306404, -9.242529, 1.2701778, 23.310083, -9.790155, -3.45827, 23.306404, -9.790155, 1.2701778, 23.306404, -13.449499, 0.0055915117, 35.369823, -17.5551, 0.37126523, 35.52769, -13.449499, 0.37126523, 35.369823, -13.449499, 0.0055915117, 35.369823, -17.5551, 0.0055915117, 35.52769, -17.5551, 0.37126523, 35.52769, -9.215921, 0.37126523, 35.583733, -13.449499, 0.0055915117, 35.369823, -13.449499, 0.37126523, 35.369823, -9.215921, 0.37126523, 35.583733, -9.215921, 0.005591452, 35.583733, -13.449499, 0.0055915117, 35.369823, -13.442921, 0.005591631, 34.39108, -17.5551, 0.0055915117, 35.52769, -13.449499, 0.0055915117, 35.369823, -13.442921, 0.005591631, 34.39108, -17.54952, 0.0055915713, 34.697742, -17.5551, 0.0055915117, 35.52769, -13.449499, 0.0055915117, 35.369823, -9.208305, 0.005591631, 34.45052, -13.442921, 0.005591631, 34.39108, -13.449499, 0.0055915117, 35.369823, -9.212177, 0.0055915713, 35.02651, -9.208305, 0.005591631, 34.45052, -9.212177, 0.0055915713, 35.02651, -13.449499, 0.0055915117, 35.369823, -9.215921, 0.005591452, 35.583733, -17.54952, 0.0055915713, 34.697742, -17.5551, 0.37126523, 35.52769, -17.5551, 0.0055915117, 35.52769, -17.54952, 0.0055915713, 34.697742, -17.54952, 0.37126535, 34.697742, -17.5551, 0.37126523, 35.52769, -9.212622, 0.37126523, 35.092834, -9.371677, 0.26217395, 34.99489, -9.212177, 0.0055915713, 35.02651, -13.444563, 0.37126535, 34.635437, -13.442921, 0.280065, 34.39108, -12.384375, 0.37126535, 34.40561, -13.444563, 0.37126535, 34.635437, -14.469788, 0.37126535, 34.467525, -13.442921, 0.280065, 34.39108, -13.444563, 0.37126535, 34.635437, -17.54952, 0.37126535, 34.697742, -14.469788, 0.37126535, 34.467525, -13.444563, 0.37126535, 34.635437, -17.5551, 0.37126523, 35.52769, -17.54952, 0.37126535, 34.697742, -17.5551, 0.37126523, 35.52769, -13.444563, 0.37126535, 34.635437, -13.449499, 0.37126523, 35.369823, -12.384375, 0.37126535, 34.40561, -13.449499, 0.37126523, 35.369823, -13.444563, 0.37126535, 34.635437, -13.449499, 0.37126523, 35.369823, -9.212622, 0.37126523, 35.092834, -9.215921, 0.37126523, 35.583733, -13.449499, 0.37126523, 35.369823, -9.939795, 0.37126523, 34.988453, -9.212622, 0.37126523, 35.092834, -12.384375, 0.37126535, 34.40561, -9.939795, 0.37126523, 34.988453, -13.449499, 0.37126523, 35.369823, -9.371677, 0.26217395, 34.99489, -9.212622, 0.37126523, 35.092834, -9.939795, 0.37126523, 34.988453, -9.21077, 0.37126535, 34.81706, -9.371677, 0.26217395, 34.99489, -9.939795, 0.37126523, 34.988453, -9.939795, 0.37126523, 34.988453, -12.384375, 0.37126535, 34.40561, -9.21077, 0.37126535, 34.81706, -9.21077, 0.37126535, 34.81706, -12.384375, 0.37126535, 34.40561, -9.208305, 0.37126535, 34.45052, -9.215921, 0.005591452, 35.583733, -9.212622, 0.37126523, 35.092834, -9.212177, 0.0055915713, 35.02651, -9.215921, 0.005591452, 35.583733, -9.215921, 0.37126523, 35.583733, -9.212622, 0.37126523, 35.092834, -9.21077, 0.37126535, 34.81706, -9.208305, 0.005591631, 34.45052, -9.212177, 0.0055915713, 35.02651, -9.21077, 0.37126535, 34.81706, -9.208305, 0.37126535, 34.45052, -9.208305, 0.005591631, 34.45052, -9.371677, 0.26217395, 34.99489, -9.21077, 0.37126535, 34.81706, -9.212177, 0.0055915713, 35.02651, -9.208305, 0.37126535, 34.45052, -13.442921, 0.005591631, 34.39108, -9.208305, 0.005591631, 34.45052, -13.442921, 0.005591631, 34.39108, -9.208305, 0.37126535, 34.45052, -12.384375, 0.37126535, 34.40561, -13.442921, 0.005591631, 34.39108, -12.384375, 0.37126535, 34.40561, -13.442921, 0.280065, 34.39108, -13.442921, 0.280065, 34.39108, -17.54952, 0.0055915713, 34.697742, -13.442921, 0.005591631, 34.39108, -13.442921, 0.280065, 34.39108, -17.54952, 0.37126535, 34.697742, -17.54952, 0.0055915713, 34.697742, -17.54952, 0.37126535, 34.697742, -13.442921, 0.280065, 34.39108, -14.469788, 0.37126535, 34.467525, -10.702703, 0.005590558, 43.383537, -17.802399, 0.37126428, 43.495537, -10.702703, 0.37126428, 43.383537, -10.702703, 0.005590558, 43.383537, -17.802399, 0.005590558, 43.495537, -17.802399, 0.37126428, 43.495537, -9.074274, 0.37126428, 43.401463, -10.702703, 0.005590558, 43.383537, -10.702703, 0.37126428, 43.383537, -9.074274, 0.37126428, 43.401463, -9.074274, 0.005590558, 43.401463, -10.702703, 0.005590558, 43.383537, -10.695927, 0.0055906773, 42.375122, -17.802399, 0.005590558, 43.495537, -10.702703, 0.005590558, 43.383537, -10.695927, 0.0055906773, 42.375122, -17.795794, 0.0055906773, 42.512863, -17.802399, 0.005590558, 43.495537, -9.074274, 0.005590558, 43.401463, -10.695927, 0.0055906773, 42.375122, -10.702703, 0.005590558, 43.383537, -9.074274, 0.005590558, 43.401463, -9.068697, 0.0055906773, 42.571514, -10.695927, 0.0055906773, 42.375122, -9.074274, 0.005590558, 43.401463, -9.068697, 0.3712644, 42.571514, -9.068697, 0.0055906773, 42.571514, -9.074274, 0.005590558, 43.401463, -9.074274, 0.37126428, 43.401463, -9.068697, 0.3712644, 42.571514, -10.697622, 0.3712644, 42.627335, -10.695927, 0.28006405, 42.375122, -10.289118, 0.3712644, 42.42411, -10.697622, 0.3712644, 42.627335, -12.470892, 0.3712644, 42.409447, -10.695927, 0.28006405, 42.375122, -10.697622, 0.3712644, 42.627335, -16.464678, 0.3712644, 42.4869, -12.470892, 0.3712644, 42.409447, -10.289118, 0.3712644, 42.42411, -10.702703, 0.37126428, 43.383537, -10.697622, 0.3712644, 42.627335, -10.289118, 0.3712644, 42.42411, -9.074274, 0.37126428, 43.401463, -10.702703, 0.37126428, 43.383537, -9.074274, 0.37126428, 43.401463, -10.289118, 0.3712644, 42.42411, -9.068697, 0.3712644, 42.571514, -17.802399, 0.37126428, 43.495537, -10.697622, 0.3712644, 42.627335, -10.702703, 0.37126428, 43.383537, -10.697622, 0.3712644, 42.627335, -17.802399, 0.37126428, 43.495537, -16.464678, 0.3712644, 42.4869, -16.464678, 0.3712644, 42.4869, -17.802399, 0.37126428, 43.495537, -17.797441, 0.3712644, 42.758095, -16.464678, 0.3712644, 42.4869, -17.797441, 0.3712644, 42.758095, -17.795794, 0.28006405, 42.512863, -9.068697, 0.3712644, 42.571514, -10.695927, 0.0055906773, 42.375122, -9.068697, 0.0055906773, 42.571514, -10.695927, 0.0055906773, 42.375122, -9.068697, 0.3712644, 42.571514, -10.289118, 0.3712644, 42.42411, -10.695927, 0.0055906773, 42.375122, -10.289118, 0.3712644, 42.42411, -10.695927, 0.28006405, 42.375122, -10.695927, 0.28006405, 42.375122, -17.795794, 0.0055906773, 42.512863, -10.695927, 0.0055906773, 42.375122, -10.695927, 0.28006405, 42.375122, -16.464678, 0.3712644, 42.4869, -17.795794, 0.0055906773, 42.512863, -17.795794, 0.0055906773, 42.512863, -16.464678, 0.3712644, 42.4869, -17.795794, 0.28006405, 42.512863, -16.464678, 0.3712644, 42.4869, -10.695927, 0.28006405, 42.375122, -12.470892, 0.3712644, 42.409447, -17.795794, 0.28006405, 42.512863, -17.802399, 0.005590558, 43.495537, -17.795794, 0.0055906773, 42.512863, -17.802399, 0.005590558, 43.495537, -17.795794, 0.28006405, 42.512863, -17.797441, 0.3712644, 42.758095, -17.802399, 0.005590558, 43.495537, -17.797441, 0.3712644, 42.758095, -17.802399, 0.37126428, 43.495537, -10.597399, 0.005591333, 36.740005, -17.854618, 0.371265, 36.850945, -10.597399, 0.3712651, 36.740005, -10.597399, 0.005591333, 36.740005, -17.854618, 0.005591333, 36.850945, -17.854618, 0.371265, 36.850945, -8.932753, 0.3712651, 36.758175, -10.597399, 0.005591333, 36.740005, -10.597399, 0.3712651, 36.740005, -8.932753, 0.3712651, 36.758175, -8.932753, 0.005591333, 36.758175, -10.597399, 0.005591333, 36.740005, -10.590622, 0.005591452, 35.73159, -17.854618, 0.005591333, 36.850945, -10.597399, 0.005591333, 36.740005, -10.590622, 0.005591452, 35.73159, -17.848013, 0.005591452, 35.868275, -17.854618, 0.005591333, 36.850945, -8.932753, 0.005591333, 36.758175, -10.590622, 0.005591452, 35.73159, -10.597399, 0.005591333, 36.740005, -8.932753, 0.005591333, 36.758175, -8.927174, 0.005591452, 35.928226, -10.590622, 0.005591452, 35.73159, -8.932753, 0.005591333, 36.758175, -8.927174, 0.3712651, 35.928226, -8.927174, 0.005591452, 35.928226, -8.932753, 0.005591333, 36.758175, -8.932753, 0.3712651, 36.758175, -8.927174, 0.3712651, 35.928226, -10.592318, 0.3712651, 35.983803, -10.590622, 0.28006488, 35.73159, -10.17465, 0.37126523, 35.78064, -10.592318, 0.3712651, 35.983803, -12.40486, 0.37126523, 35.76565, -10.590622, 0.28006488, 35.73159, -10.592318, 0.3712651, 35.983803, -16.487228, 0.3712651, 35.842945, -12.40486, 0.37126523, 35.76565, -10.17465, 0.37126523, 35.78064, -10.597399, 0.3712651, 36.740005, -10.592318, 0.3712651, 35.983803, -10.17465, 0.37126523, 35.78064, -8.932753, 0.3712651, 36.758175, -10.597399, 0.3712651, 36.740005, -8.932753, 0.3712651, 36.758175, -10.17465, 0.37126523, 35.78064, -8.927174, 0.3712651, 35.928226, -17.854618, 0.371265, 36.850945, -10.592318, 0.3712651, 35.983803, -10.597399, 0.3712651, 36.740005, -10.592318, 0.3712651, 35.983803, -17.854618, 0.371265, 36.850945, -16.487228, 0.3712651, 35.842945, -16.487228, 0.3712651, 35.842945, -17.854618, 0.371265, 36.850945, -17.849669, 0.3712651, 36.11438, -16.487228, 0.3712651, 35.842945, -17.849669, 0.3712651, 36.11438, -17.848013, 0.28006476, 35.868275, -8.927174, 0.3712651, 35.928226, -10.590622, 0.005591452, 35.73159, -8.927174, 0.005591452, 35.928226, -10.590622, 0.005591452, 35.73159, -8.927174, 0.3712651, 35.928226, -10.17465, 0.37126523, 35.78064, -10.590622, 0.005591452, 35.73159, -10.17465, 0.37126523, 35.78064, -10.590622, 0.28006488, 35.73159, -10.590622, 0.28006488, 35.73159, -17.848013, 0.005591452, 35.868275, -10.590622, 0.005591452, 35.73159, -10.590622, 0.28006488, 35.73159, -16.487228, 0.3712651, 35.842945, -17.848013, 0.005591452, 35.868275, -17.848013, 0.005591452, 35.868275, -16.487228, 0.3712651, 35.842945, -17.848013, 0.28006476, 35.868275, -16.487228, 0.3712651, 35.842945, -10.590622, 0.28006488, 35.73159, -12.40486, 0.37126523, 35.76565, -17.848013, 0.28006476, 35.868275, -17.854618, 0.005591333, 36.850945, -17.848013, 0.005591452, 35.868275, -17.854618, 0.005591333, 36.850945, -17.848013, 0.28006476, 35.868275, -17.849669, 0.3712651, 36.11438, -17.854618, 0.005591333, 36.850945, -17.849669, 0.3712651, 36.11438, -17.854618, 0.371265, 36.850945, -16.774326, -3.4582698, 22.20213, -17.504349, 1.2701781, 22.197224, -17.504349, -3.4582698, 22.197224, -16.774326, -3.4582698, 22.20213, -16.774326, 0.72166747, 22.20213, -17.504349, 1.2701781, 22.197224, -16.774326, 0.72166747, 22.20213, -17.509253, 1.2701778, 22.926807, -17.504349, 1.2701781, 22.197224, -16.774326, 0.72166747, 22.20213, -16.77923, 1.2701778, 22.931713, -17.509253, 1.2701778, 22.926807, -16.77923, 1.2701778, 22.931713, -17.509253, -3.45827, 22.926807, -17.509253, 1.2701778, 22.926807, -16.77923, 1.2701778, 22.931713, -16.77923, -3.45827, 22.931713, -17.509253, -3.45827, 22.926807, -16.77923, -3.45827, 22.931713, -17.504349, -3.4582698, 22.197224, -17.509253, -3.45827, 22.926807, -16.77923, -3.45827, 22.931713, -16.774326, -3.4582698, 22.20213, -17.504349, -3.4582698, 22.197224, -17.504349, -3.4582698, 22.197224, -17.509253, 1.2701778, 22.926807, -17.509253, -3.45827, 22.926807, -17.504349, -3.4582698, 22.197224, -17.504349, 1.2701781, 22.197224, -17.509253, 1.2701778, 22.926807, -16.77923, -3.45827, 22.931713, -16.774326, 0.72166747, 22.20213, -16.774326, -3.4582698, 22.20213, -16.77923, -3.45827, 22.931713, -16.77923, 1.2701778, 22.931713, -16.774326, 0.72166747, 22.20213, -16.92366, -0.19077104, 22.930307, -17.155365, -0.09826177, 22.928751, -17.155365, -0.19077104, 22.928751, -16.92366, -0.19077104, 22.930307, -16.92366, -0.09826177, 22.930307, -17.155365, -0.09826177, 22.928751, -16.92366, -0.09826177, 22.930307, -17.320957, -0.098264754, 47.5684, -17.155365, -0.09826177, 22.928751, -16.92366, -0.09826177, 22.930307, -17.089252, -0.098264754, 47.569958, -17.320957, -0.098264754, 47.5684, -17.089252, -0.098264754, 47.569958, -17.320957, -0.19077402, 47.5684, -17.320957, -0.098264754, 47.5684, -17.089252, -0.098264754, 47.569958, -17.089252, -0.19077402, 47.569958, -17.320957, -0.19077402, 47.5684, -17.089252, -0.19077402, 47.569958, -17.155365, -0.19077104, 22.928751, -17.320957, -0.19077402, 47.5684, -17.089252, -0.19077402, 47.569958, -16.92366, -0.19077104, 22.930307, -17.155365, -0.19077104, 22.928751, -17.155365, -0.19077104, 22.928751, -17.320957, -0.098264754, 47.5684, -17.320957, -0.19077402, 47.5684, -17.155365, -0.19077104, 22.928751, -17.155365, -0.09826177, 22.928751, -17.320957, -0.098264754, 47.5684, -17.089252, -0.19077402, 47.569958, -16.92366, -0.09826177, 22.930307, -16.92366, -0.19077104, 22.930307, -17.089252, -0.19077402, 47.569958, -17.089252, -0.098264754, 47.569958, -16.92366, -0.09826177, 22.930307, -9.130678, -2.9032154, 33.80826, -9.860701, 1.8252325, 33.803356, -9.860701, -2.9032154, 33.803352, -9.130678, -2.9032154, 33.80826, -9.130678, 1.8252325, 33.80826, -9.860701, 1.8252325, 33.803356, -9.135584, -2.9032154, 34.538284, -9.860701, -2.9032154, 33.803352, -9.865606, -2.9032154, 34.53338, -9.135584, -2.9032154, 34.538284, -9.130678, -2.9032154, 33.80826, -9.860701, -2.9032154, 33.803352, -9.860701, -2.9032154, 33.803352, -9.865606, 1.8252323, 34.53338, -9.865606, -2.9032154, 34.53338, -9.860701, -2.9032154, 33.803352, -9.860701, 1.8252325, 33.803356, -9.865606, 1.8252323, 34.53338, -9.135584, 0.64312047, 34.538284, -9.317982, 1.8252323, 34.537056, -9.134359, 1.8252323, 34.35589, -9.130678, -2.9032154, 33.80826, -9.135584, 0.64312047, 34.538284, -9.134359, 1.8252323, 34.35589, -9.135584, 0.64312047, 34.538284, -9.130678, -2.9032154, 33.80826, -9.135584, -2.9032154, 34.538284, -9.130678, -2.9032154, 33.80826, -9.134359, 1.8252323, 34.35589, -9.130678, 1.8252325, 33.80826, -9.134359, 1.8252323, 34.35589, -9.860701, 1.8252325, 33.803356, -9.130678, 1.8252325, 33.80826, -9.860701, 1.8252325, 33.803356, -9.134359, 1.8252323, 34.35589, -9.317982, 1.8252323, 34.537056, -9.860701, 1.8252325, 33.803356, -9.317982, 1.8252323, 34.537056, -9.865606, 1.8252323, 34.53338, -9.135584, -2.9032154, 34.538284, -9.317982, 1.8252323, 34.537056, -9.135584, 0.64312047, 34.538284, -9.317982, 1.8252323, 34.537056, -9.135584, -2.9032154, 34.538284, -9.865606, -2.9032154, 34.53338, -9.317982, 1.8252323, 34.537056, -9.865606, -2.9032154, 34.53338, -9.865606, 1.8252323, 34.53338, -9.18182, -3.7899094, 41.417854, -9.91184, 0.9385386, 41.41295, -9.91184, -3.7899094, 41.41295, -9.18182, -3.7899094, 41.417854, -9.18182, 0.9385386, 41.417854, -9.91184, 0.9385386, 41.41295, -9.186722, -3.7899094, 42.147438, -9.91184, -3.7899094, 41.41295, -9.916744, -3.7899094, 42.142532, -9.186722, -3.7899094, 42.147438, -9.18182, -3.7899094, 41.417854, -9.91184, -3.7899094, 41.41295, -9.91184, -3.7899094, 41.41295, -9.916744, 0.9385385, 42.142532, -9.916744, -3.7899094, 42.142532, -9.91184, -3.7899094, 41.41295, -9.91184, 0.9385386, 41.41295, -9.916744, 0.9385385, 42.142532, -9.186722, -0.24357355, 42.147438, -9.36912, 0.9385385, 42.146214, -9.185497, 0.9385385, 41.965042, -9.18182, -3.7899094, 41.417854, -9.186722, -0.24357355, 42.147438, -9.185497, 0.9385385, 41.965042, -9.186722, -0.24357355, 42.147438, -9.18182, -3.7899094, 41.417854, -9.186722, -3.7899094, 42.147438, -9.18182, -3.7899094, 41.417854, -9.185497, 0.9385385, 41.965042, -9.18182, 0.9385386, 41.417854, -9.185497, 0.9385385, 41.965042, -9.91184, 0.9385386, 41.41295, -9.18182, 0.9385386, 41.417854, -9.91184, 0.9385386, 41.41295, -9.185497, 0.9385385, 41.965042, -9.36912, 0.9385385, 42.146214, -9.91184, 0.9385386, 41.41295, -9.36912, 0.9385385, 42.146214, -9.916744, 0.9385385, 42.142532, -9.186722, -3.7899094, 42.147438, -9.36912, 0.9385385, 42.146214, -9.186722, -0.24357355, 42.147438, -9.36912, 0.9385385, 42.146214, -9.186722, -3.7899094, 42.147438, -9.916744, -3.7899094, 42.142532, -9.36912, 0.9385385, 42.146214, -9.916744, -3.7899094, 42.142532, -9.916744, 0.9385385, 42.142532, -17.459593, -3.5494714, 33.653664, -18.189178, 1.1789763, 33.64876, -18.189178, -3.5494714, 33.64876, -17.459593, -3.5494714, 33.653664, -17.459593, 1.1789763, 33.653664, -18.189178, 1.1789763, 33.64876, -17.4645, -3.5494716, 34.383686, -18.189178, -3.5494714, 33.64876, -18.194086, -3.5494716, 34.378784, -17.4645, -3.5494716, 34.383686, -17.459593, -3.5494714, 33.653664, -18.189178, -3.5494714, 33.64876, -18.189178, -3.5494714, 33.64876, -18.194086, 1.178976, 34.378784, -18.194086, -3.5494716, 34.378784, -18.189178, -3.5494714, 33.64876, -18.189178, 1.1789763, 33.64876, -18.194086, 1.178976, 34.378784, -17.4645, -0.0031356215, 34.383686, -17.646896, 1.178976, 34.38246, -17.463274, 1.1789763, 34.20129, -17.459593, -3.5494714, 33.653664, -17.4645, -0.0031356215, 34.383686, -17.463274, 1.1789763, 34.20129, -17.4645, -0.0031356215, 34.383686, -17.459593, -3.5494714, 33.653664, -17.4645, -3.5494716, 34.383686, -17.459593, -3.5494714, 33.653664, -17.463274, 1.1789763, 34.20129, -17.459593, 1.1789763, 33.653664, -17.463274, 1.1789763, 34.20129, -18.189178, 1.1789763, 33.64876, -17.459593, 1.1789763, 33.653664, -18.189178, 1.1789763, 33.64876, -17.463274, 1.1789763, 34.20129, -17.646896, 1.178976, 34.38246, -18.189178, 1.1789763, 33.64876, -17.646896, 1.178976, 34.38246, -18.194086, 1.178976, 34.378784, -17.4645, -3.5494716, 34.383686, -17.646896, 1.178976, 34.38246, -17.4645, -0.0031356215, 34.383686, -17.646896, 1.178976, 34.38246, -17.4645, -3.5494716, 34.383686, -18.194086, -3.5494716, 34.378784, -17.646896, 1.178976, 34.38246, -18.194086, -3.5494716, 34.378784, -18.194086, 1.178976, 34.378784, -17.511396, -3.5494723, 41.361874, -18.240982, 1.1789753, 41.35697, -18.240982, -3.5494723, 41.35697, -17.511396, -3.5494723, 41.361874, -17.511396, 1.1789753, 41.361874, -18.240982, 1.1789753, 41.35697, -17.5163, -3.5494723, 42.09146, -18.240982, -3.5494723, 41.35697, -18.245886, -3.5494723, 42.086555, -17.5163, -3.5494723, 42.09146, -17.511396, -3.5494723, 41.361874, -18.240982, -3.5494723, 41.35697, -18.240982, -3.5494723, 41.35697, -18.245886, 1.1789753, 42.086555, -18.245886, -3.5494723, 42.086555, -18.240982, -3.5494723, 41.35697, -18.240982, 1.1789753, 41.35697, -18.245886, 1.1789753, 42.086555, -17.5163, -0.0031365752, 42.09146, -17.698698, 1.1789753, 42.090233, -17.515074, 1.1789753, 41.909065, -17.511396, -3.5494723, 41.361874, -17.5163, -0.0031365752, 42.09146, -17.515074, 1.1789753, 41.909065, -17.5163, -0.0031365752, 42.09146, -17.511396, -3.5494723, 41.361874, -17.5163, -3.5494723, 42.09146, -17.511396, -3.5494723, 41.361874, -17.515074, 1.1789753, 41.909065, -17.511396, 1.1789753, 41.361874, -17.515074, 1.1789753, 41.909065, -18.240982, 1.1789753, 41.35697, -17.511396, 1.1789753, 41.361874, -18.240982, 1.1789753, 41.35697, -17.515074, 1.1789753, 41.909065, -17.698698, 1.1789753, 42.090233, -18.240982, 1.1789753, 41.35697, -17.698698, 1.1789753, 42.090233, -18.245886, 1.1789753, 42.086555, -17.5163, -3.5494723, 42.09146, -17.698698, 1.1789753, 42.090233, -17.5163, -0.0031365752, 42.09146, -17.698698, 1.1789753, 42.090233, -17.5163, -3.5494723, 42.09146, -18.245886, -3.5494723, 42.086555, -17.698698, 1.1789753, 42.090233, -18.245886, -3.5494723, 42.086555, -18.245886, 1.1789753, 42.086555, -12.414185, -22.456995, 9.651152, -10.526909, -22.242655, 9.475412, -12.414185, -22.326996, 9.5211525, -12.414185, -22.456995, 9.651152, -10.526909, -22.391994, 9.624752, -10.526909, -22.242655, 9.475412, -19.07084, -22.391996, 9.624752, -12.414185, -22.456995, 9.651152, -12.414185, -22.326996, 9.5211525, -19.07084, -22.391996, 9.624752, -12.414185, -22.326996, 9.5211525, -19.07084, -22.242657, 9.475412, -12.414185, -22.326996, 9.5211525, -10.526909, -22.03864, 9.420736, -12.414185, -22.14938, 9.473576, -12.414185, -22.326996, 9.5211525, -10.526909, -22.242655, 9.475412, -10.526909, -22.03864, 9.420736, -19.07084, -22.242657, 9.475412, -12.414185, -22.326996, 9.5211525, -12.414185, -22.14938, 9.473576, -19.07084, -22.242657, 9.475412, -12.414185, -22.14938, 9.473576, -19.07084, -22.03864, 9.420735, -12.414185, -22.14938, 9.473576, -10.526909, -21.834621, 9.475412, -12.414185, -21.971802, 9.5211525, -12.414185, -22.14938, 9.473576, -10.526909, -22.03864, 9.420736, -10.526909, -21.834621, 9.475412, -19.07084, -22.03864, 9.420735, -12.414185, -22.14938, 9.473576, -12.414185, -21.971802, 9.5211525, -19.07084, -22.03864, 9.420735, -12.414185, -21.971802, 9.5211525, -19.07084, -21.834623, 9.475412, -12.414185, -21.971802, 9.5211525, -10.526909, -21.685282, 9.624752, -12.414185, -21.841803, 9.651152, -12.414185, -21.971802, 9.5211525, -10.526909, -21.834621, 9.475412, -10.526909, -21.685282, 9.624752, -19.07084, -21.834623, 9.475412, -12.414185, -21.971802, 9.5211525, -12.414185, -21.841803, 9.651152, -19.07084, -21.834623, 9.475412, -12.414185, -21.841803, 9.651152, -19.07084, -21.685282, 9.624752, -12.414185, -21.841803, 9.651152, -10.526909, -21.630606, 9.82877, -12.414185, -21.794186, 9.82877, -12.414185, -21.841803, 9.651152, -10.526909, -21.685282, 9.624752, -10.526909, -21.630606, 9.82877, -19.07084, -21.685282, 9.624752, -12.414185, -21.841803, 9.651152, -12.414185, -21.794186, 9.82877, -19.07084, -21.685282, 9.624752, -12.414185, -21.794186, 9.82877, -19.07084, -21.630606, 9.828769, -12.414185, -21.794186, 9.82877, -10.526909, -21.685282, 10.032785, -12.414185, -21.841803, 10.006346, -12.414185, -21.794186, 9.82877, -10.526909, -21.630606, 9.82877, -10.526909, -21.685282, 10.032785, -19.07084, -21.630606, 9.828769, -12.414185, -21.794186, 9.82877, -12.414185, -21.841803, 10.006346, -19.07084, -21.630606, 9.828769, -12.414185, -21.841803, 10.006346, -19.07084, -21.685282, 10.032785, -12.414185, -21.841803, 10.006346, -10.526909, -21.834621, 10.182125, -12.414185, -21.971802, 10.136345, -12.414185, -21.841803, 10.006346, -10.526909, -21.685282, 10.032785, -10.526909, -21.834621, 10.182125, -19.07084, -21.685282, 10.032785, -12.414185, -21.841803, 10.006346, -12.414185, -21.971802, 10.136345, -19.07084, -21.685282, 10.032785, -12.414185, -21.971802, 10.136345, -19.07084, -21.834623, 10.182125, -12.414185, -21.971802, 10.136345, -10.526909, -22.03864, 10.236803, -12.414185, -22.149378, 10.183962, -12.414185, -21.971802, 10.136345, -10.526909, -21.834621, 10.182125, -10.526909, -22.03864, 10.236803, -19.07084, -21.834623, 10.182125, -12.414185, -21.971802, 10.136345, -12.414185, -22.149378, 10.183962, -19.07084, -21.834623, 10.182125, -12.414185, -22.149378, 10.183962, -19.07084, -22.03864, 10.236801, -12.414185, -22.149378, 10.183962, -10.526909, -22.242655, 10.182125, -12.414185, -22.326996, 10.136345, -12.414185, -22.149378, 10.183962, -10.526909, -22.03864, 10.236803, -10.526909, -22.242655, 10.182125, -19.07084, -22.03864, 10.236801, -12.414185, -22.149378, 10.183962, -12.414185, -22.326996, 10.136345, -19.07084, -22.03864, 10.236801, -12.414185, -22.326996, 10.136345, -19.07084, -22.242657, 10.182125, -12.414185, -22.326996, 10.136345, -10.526909, -22.391994, 10.032785, -12.414185, -22.456995, 10.006346, -12.414185, -22.326996, 10.136345, -10.526909, -22.242655, 10.182125, -10.526909, -22.391994, 10.032785, -19.07084, -22.242657, 10.182125, -12.414185, -22.326996, 10.136345, -12.414185, -22.456995, 10.006346, -19.07084, -22.242657, 10.182125, -12.414185, -22.456995, 10.006346, -19.07084, -22.391996, 10.032785, -12.414185, -22.456995, 10.006346, -10.526909, -22.446672, 9.82877, -12.414185, -22.504572, 9.82877, -12.414185, -22.456995, 10.006346, -10.526909, -22.391994, 10.032785, -10.526909, -22.446672, 9.82877, -19.07084, -22.391996, 10.032785, -12.414185, -22.456995, 10.006346, -12.414185, -22.504572, 9.82877, -19.07084, -22.391996, 10.032785, -12.414185, -22.504572, 9.82877, -19.07084, -22.446672, 9.828769, -12.414185, -22.504572, 9.82877, -10.526909, -22.391994, 9.624752, -12.414185, -22.456995, 9.651152, -12.414185, -22.504572, 9.82877, -10.526909, -22.446672, 9.82877, -10.526909, -22.391994, 9.624752, -19.07084, -22.446672, 9.828769, -12.414185, -22.504572, 9.82877, -12.414185, -22.456995, 9.651152, -19.07084, -22.446672, 9.828769, -12.414185, -22.456995, 9.651152, -19.07084, -22.391996, 9.624752, -19.07084, -22.242657, 9.475412, -19.693949, -22.03864, 9.828769, -19.07084, -22.391996, 9.624752, -19.07084, -22.391996, 9.624752, -19.693949, -22.03864, 9.828769, -19.07084, -22.446672, 9.828769, -19.07084, -22.446672, 9.828769, -19.693949, -22.03864, 9.828769, -19.07084, -22.391996, 10.032785, -19.07084, -22.391996, 10.032785, -19.693949, -22.03864, 9.828769, -19.07084, -22.242657, 10.182125, -19.07084, -22.03864, 9.420735, -19.693949, -22.03864, 9.828769, -19.07084, -22.242657, 9.475412, -19.07084, -21.834623, 9.475412, -19.693949, -22.03864, 9.828769, -19.07084, -22.03864, 9.420735, -19.07084, -21.685282, 9.624752, -19.693949, -22.03864, 9.828769, -19.07084, -21.834623, 9.475412, -19.07084, -21.630606, 9.828769, -19.693949, -22.03864, 9.828769, -19.07084, -21.685282, 9.624752, -19.07084, -21.685282, 10.032785, -19.693949, -22.03864, 9.828769, -19.07084, -21.630606, 9.828769, -19.07084, -21.834623, 10.182125, -19.693949, -22.03864, 9.828769, -19.07084, -21.685282, 10.032785, -19.07084, -22.03864, 10.236801, -19.693949, -22.03864, 9.828769, -19.07084, -21.834623, 10.182125, -19.07084, -22.242657, 10.182125, -19.693949, -22.03864, 9.828769, -19.07084, -22.03864, 10.236801, -10.526909, -22.391994, 9.624752, -9.89662, -22.03864, 9.82877, -10.526909, -22.242655, 9.475412, -10.526909, -22.446672, 9.82877, -9.89662, -22.03864, 9.82877, -10.526909, -22.391994, 9.624752, -10.526909, -22.391994, 10.032785, -9.89662, -22.03864, 9.82877, -10.526909, -22.446672, 9.82877, -10.526909, -22.242655, 10.182125, -9.89662, -22.03864, 9.82877, -10.526909, -22.391994, 10.032785, -10.526909, -22.242655, 9.475412, -9.89662, -22.03864, 9.82877, -10.526909, -22.03864, 9.420736, -10.526909, -22.03864, 9.420736, -9.89662, -22.03864, 9.82877, -10.526909, -21.834621, 9.475412, -10.526909, -21.834621, 9.475412, -9.89662, -22.03864, 9.82877, -10.526909, -21.685282, 9.624752, -10.526909, -21.685282, 9.624752, -9.89662, -22.03864, 9.82877, -10.526909, -21.630606, 9.82877, -10.526909, -21.630606, 9.82877, -9.89662, -22.03864, 9.82877, -10.526909, -21.685282, 10.032785, -10.526909, -21.685282, 10.032785, -9.89662, -22.03864, 9.82877, -10.526909, -21.834621, 10.182125, -10.526909, -21.834621, 10.182125, -9.89662, -22.03864, 9.82877, -10.526909, -22.03864, 10.236803, -10.526909, -22.03864, 10.236803, -9.89662, -22.03864, 9.82877, -10.526909, -22.242655, 10.182125, -14.262087, -22.348663, 7.7697797, -10.526909, -22.242655, 7.6348023, -14.262087, -22.188835, 7.609912, -14.262087, -22.348663, 7.7697797, -10.526909, -22.391994, 7.7841425, -10.526909, -22.242655, 7.6348023, -19.07084, -22.391996, 7.784142, -14.262087, -22.348663, 7.7697797, -14.262087, -22.188835, 7.609912, -19.07084, -22.391996, 7.784142, -14.262087, -22.188835, 7.609912, -19.07084, -22.242657, 7.634802, -14.262087, -22.188835, 7.609912, -10.526909, -22.03864, 7.580126, -14.262087, -21.970457, 7.5514, -14.262087, -22.188835, 7.609912, -10.526909, -22.242655, 7.6348023, -10.526909, -22.03864, 7.580126, -19.07084, -22.242657, 7.634802, -14.262087, -22.188835, 7.609912, -14.262087, -21.970457, 7.5514, -19.07084, -22.242657, 7.634802, -14.262087, -21.970457, 7.5514, -19.07084, -22.03864, 7.5801253, -14.262087, -21.970457, 7.5514, -10.526909, -21.834621, 7.6348023, -14.262087, -21.752077, 7.609912, -14.262087, -21.970457, 7.5514, -10.526909, -22.03864, 7.580126, -10.526909, -21.834621, 7.6348023, -19.07084, -22.03864, 7.5801253, -14.262087, -21.970457, 7.5514, -14.262087, -21.752077, 7.609912, -19.07084, -22.03864, 7.5801253, -14.262087, -21.752077, 7.609912, -19.07084, -21.834623, 7.634802, -14.262087, -21.752077, 7.609912, -10.526909, -21.685282, 7.7841425, -14.262087, -21.592209, 7.769779, -14.262087, -21.752077, 7.609912, -10.526909, -21.834621, 7.6348023, -10.526909, -21.685282, 7.7841425, -19.07084, -21.834623, 7.634802, -14.262087, -21.752077, 7.609912, -14.262087, -21.592209, 7.769779, -19.07084, -21.834623, 7.634802, -14.262087, -21.592209, 7.769779, -19.07084, -21.685282, 7.784142, -14.262087, -21.592209, 7.769779, -10.526909, -21.630606, 7.988159, -14.262087, -21.533697, 7.9881587, -14.262087, -21.592209, 7.769779, -10.526909, -21.685282, 7.7841425, -10.526909, -21.630606, 7.988159, -19.07084, -21.685282, 7.784142, -14.262087, -21.592209, 7.769779, -14.262087, -21.533697, 7.9881587, -19.07084, -21.685282, 7.784142, -14.262087, -21.533697, 7.9881587, -19.07084, -21.630606, 7.9881587, -14.262087, -21.533697, 7.9881587, -10.526909, -21.685282, 8.192176, -14.262087, -21.592209, 8.206538, -14.262087, -21.533697, 7.9881587, -10.526909, -21.630606, 7.988159, -10.526909, -21.685282, 8.192176, -19.07084, -21.630606, 7.9881587, -14.262087, -21.533697, 7.9881587, -14.262087, -21.592209, 8.206538, -19.07084, -21.630606, 7.9881587, -14.262087, -21.592209, 8.206538, -19.07084, -21.685282, 8.192175, -14.262087, -21.592209, 8.206538, -10.526909, -21.834621, 8.3415165, -14.262087, -21.752077, 8.366365, -14.262087, -21.592209, 8.206538, -10.526909, -21.685282, 8.192176, -10.526909, -21.834621, 8.3415165, -19.07084, -21.685282, 8.192175, -14.262087, -21.592209, 8.206538, -14.262087, -21.752077, 8.366365, -19.07084, -21.685282, 8.192175, -14.262087, -21.752077, 8.366365, -19.07084, -21.834623, 8.341516, -14.262087, -21.752077, 8.366365, -10.526909, -22.03864, 8.396193, -14.262087, -21.970457, 8.424917, -14.262087, -21.752077, 8.366365, -10.526909, -21.834621, 8.3415165, -10.526909, -22.03864, 8.396193, -19.07084, -21.834623, 8.341516, -14.262087, -21.752077, 8.366365, -14.262087, -21.970457, 8.424917, -19.07084, -21.834623, 8.341516, -14.262087, -21.970457, 8.424917, -19.07084, -22.03864, 8.396193, -14.262087, -21.970457, 8.424917, -10.526909, -22.242655, 8.3415165, -14.262087, -22.188835, 8.366365, -14.262087, -21.970457, 8.424917, -10.526909, -22.03864, 8.396193, -10.526909, -22.242655, 8.3415165, -19.07084, -22.03864, 8.396193, -14.262087, -21.970457, 8.424917, -14.262087, -22.188835, 8.366365, -19.07084, -22.03864, 8.396193, -14.262087, -22.188835, 8.366365, -19.07084, -22.242657, 8.341516, -14.262087, -22.188835, 8.366365, -10.526909, -22.391994, 8.192176, -14.262087, -22.348663, 8.206538, -14.262087, -22.188835, 8.366365, -10.526909, -22.242655, 8.3415165, -10.526909, -22.391994, 8.192176, -19.07084, -22.242657, 8.341516, -14.262087, -22.188835, 8.366365, -14.262087, -22.348663, 8.206538, -19.07084, -22.242657, 8.341516, -14.262087, -22.348663, 8.206538, -19.07084, -22.391996, 8.192175, -14.262087, -22.348663, 8.206538, -10.526909, -22.446672, 7.988159, -14.262087, -22.407175, 7.9881587, -14.262087, -22.348663, 8.206538, -10.526909, -22.391994, 8.192176, -10.526909, -22.446672, 7.988159, -19.07084, -22.391996, 8.192175, -14.262087, -22.348663, 8.206538, -14.262087, -22.407175, 7.9881587, -19.07084, -22.391996, 8.192175, -14.262087, -22.407175, 7.9881587, -19.07084, -22.446672, 7.9881587, -14.262087, -22.407175, 7.9881587, -10.526909, -22.391994, 7.7841425, -14.262087, -22.348663, 7.7697797, -14.262087, -22.407175, 7.9881587, -10.526909, -22.446672, 7.988159, -10.526909, -22.391994, 7.7841425, -19.07084, -22.446672, 7.9881587, -14.262087, -22.407175, 7.9881587, -14.262087, -22.348663, 7.7697797, -19.07084, -22.446672, 7.9881587, -14.262087, -22.348663, 7.7697797, -19.07084, -22.391996, 7.784142, -19.07084, -22.242657, 7.634802, -19.693949, -22.03864, 7.9881587, -19.07084, -22.391996, 7.784142, -19.07084, -22.391996, 7.784142, -19.693949, -22.03864, 7.9881587, -19.07084, -22.446672, 7.9881587, -19.07084, -22.446672, 7.9881587, -19.693949, -22.03864, 7.9881587, -19.07084, -22.391996, 8.192175, -19.07084, -22.391996, 8.192175, -19.693949, -22.03864, 7.9881587, -19.07084, -22.242657, 8.341516, -19.07084, -22.03864, 7.5801253, -19.693949, -22.03864, 7.9881587, -19.07084, -22.242657, 7.634802, -19.07084, -21.834623, 7.634802, -19.693949, -22.03864, 7.9881587, -19.07084, -22.03864, 7.5801253, -19.07084, -21.685282, 7.784142, -19.693949, -22.03864, 7.9881587, -19.07084, -21.834623, 7.634802, -19.07084, -21.630606, 7.9881587, -19.693949, -22.03864, 7.9881587, -19.07084, -21.685282, 7.784142, -19.07084, -21.685282, 8.192175, -19.693949, -22.03864, 7.9881587, -19.07084, -21.630606, 7.9881587, -19.07084, -21.834623, 8.341516, -19.693949, -22.03864, 7.9881587, -19.07084, -21.685282, 8.192175, -19.07084, -22.03864, 8.396193, -19.693949, -22.03864, 7.9881587, -19.07084, -21.834623, 8.341516, -19.07084, -22.242657, 8.341516, -19.693949, -22.03864, 7.9881587, -19.07084, -22.03864, 8.396193, -10.526909, -22.391994, 7.7841425, -9.89662, -22.03864, 7.988159, -10.526909, -22.242655, 7.6348023, -10.526909, -22.446672, 7.988159, -9.89662, -22.03864, 7.988159, -10.526909, -22.391994, 7.7841425, -10.526909, -22.391994, 8.192176, -9.89662, -22.03864, 7.988159, -10.526909, -22.446672, 7.988159, -10.526909, -22.242655, 8.3415165, -9.89662, -22.03864, 7.988159, -10.526909, -22.391994, 8.192176, -10.526909, -22.242655, 7.6348023, -9.89662, -22.03864, 7.988159, -10.526909, -22.03864, 7.580126, -10.526909, -22.03864, 7.580126, -9.89662, -22.03864, 7.988159, -10.526909, -21.834621, 7.6348023, -10.526909, -21.834621, 7.6348023, -9.89662, -22.03864, 7.988159, -10.526909, -21.685282, 7.7841425, -10.526909, -21.685282, 7.7841425, -9.89662, -22.03864, 7.988159, -10.526909, -21.630606, 7.988159, -10.526909, -21.630606, 7.988159, -9.89662, -22.03864, 7.988159, -10.526909, -21.685282, 8.192176, -10.526909, -21.685282, 8.192176, -9.89662, -22.03864, 7.988159, -10.526909, -21.834621, 8.3415165, -10.526909, -21.834621, 8.3415165, -9.89662, -22.03864, 7.988159, -10.526909, -22.03864, 8.396193, -10.526909, -22.03864, 8.396193, -9.89662, -22.03864, 7.988159, -10.526909, -22.242655, 8.3415165, -12.004166, -22.046726, 10.088959, -12.124956, -19.902678, 9.968168, -12.124956, -22.046726, 9.968168, -12.004166, -22.046726, 10.088959, -12.004166, -19.902678, 10.088959, -12.124956, -19.902678, 9.968168, -12.124956, -22.046726, 9.968168, -12.28997, -19.902678, 9.923944, -12.28997, -22.046726, 9.923944, -12.124956, -22.046726, 9.968168, -12.124956, -19.902678, 9.968168, -12.28997, -19.902678, 9.923944, -12.28997, -22.046726, 9.923944, -12.454985, -19.902678, 9.968168, -12.454985, -22.046726, 9.968168, -12.28997, -22.046726, 9.923944, -12.28997, -19.902678, 9.923944, -12.454985, -19.902678, 9.968168, -12.454985, -22.046726, 9.968168, -12.575773, -19.902678, 10.088959, -12.575773, -22.046726, 10.088959, -12.454985, -22.046726, 9.968168, -12.454985, -19.902678, 9.968168, -12.575773, -19.902678, 10.088959, -12.575773, -22.046726, 10.088959, -12.619997, -19.902678, 10.253973, -12.619997, -22.046726, 10.253973, -12.575773, -22.046726, 10.088959, -12.575773, -19.902678, 10.088959, -12.619997, -19.902678, 10.253973, -12.619997, -22.046726, 10.253973, -12.575773, -19.902678, 10.418985, -12.575773, -22.046726, 10.418987, -12.619997, -22.046726, 10.253973, -12.619997, -19.902678, 10.253973, -12.575773, -19.902678, 10.418985, -12.575773, -22.046726, 10.418987, -12.454985, -19.902678, 10.539776, -12.454985, -22.046726, 10.539778, -12.575773, -22.046726, 10.418987, -12.575773, -19.902678, 10.418985, -12.454985, -19.902678, 10.539776, -12.454985, -22.046726, 10.539778, -12.28997, -19.902678, 10.584, -12.28997, -22.046726, 10.584002, -12.454985, -22.046726, 10.539778, -12.454985, -19.902678, 10.539776, -12.28997, -19.902678, 10.584, -12.28997, -22.046726, 10.584002, -12.124956, -19.902678, 10.539776, -12.124956, -22.046726, 10.539778, -12.28997, -22.046726, 10.584002, -12.28997, -19.902678, 10.584, -12.124956, -19.902678, 10.539776, -12.124956, -22.046726, 10.539778, -12.004166, -19.902678, 10.418985, -12.004166, -22.046726, 10.418987, -12.124956, -22.046726, 10.539778, -12.124956, -19.902678, 10.539776, -12.004166, -19.902678, 10.418985, -12.004166, -22.046726, 10.418987, -11.959942, -19.902678, 10.253973, -11.959942, -22.046726, 10.253973, -12.004166, -22.046726, 10.418987, -12.004166, -19.902678, 10.418985, -11.959942, -19.902678, 10.253973, -11.959942, -22.046726, 10.253973, -12.004166, -19.902678, 10.088959, -12.004166, -22.046726, 10.088959, -11.959942, -22.046726, 10.253973, -11.959942, -19.902678, 10.253973, -12.004166, -19.902678, 10.088959, -12.124956, -22.046726, 9.968168, -12.28997, -22.046726, 10.253973, -12.004166, -22.046726, 10.088959, -12.004166, -22.046726, 10.088959, -12.28997, -22.046726, 10.253973, -11.959942, -22.046726, 10.253973, -12.28997, -22.046726, 9.923944, -12.28997, -22.046726, 10.253973, -12.124956, -22.046726, 9.968168, -11.959942, -22.046726, 10.253973, -12.28997, -22.046726, 10.253973, -12.004166, -22.046726, 10.418987, -12.454985, -22.046726, 9.968168, -12.28997, -22.046726, 10.253973, -12.28997, -22.046726, 9.923944, -12.004166, -22.046726, 10.418987, -12.28997, -22.046726, 10.253973, -12.124956, -22.046726, 10.539778, -12.575773, -22.046726, 10.088959, -12.28997, -22.046726, 10.253973, -12.454985, -22.046726, 9.968168, -12.124956, -22.046726, 10.539778, -12.28997, -22.046726, 10.253973, -12.28997, -22.046726, 10.584002, -12.619997, -22.046726, 10.253973, -12.28997, -22.046726, 10.253973, -12.575773, -22.046726, 10.088959, -12.28997, -22.046726, 10.584002, -12.28997, -22.046726, 10.253973, -12.454985, -22.046726, 10.539778, -12.575773, -22.046726, 10.418987, -12.28997, -22.046726, 10.253973, -12.619997, -22.046726, 10.253973, -12.454985, -22.046726, 10.539778, -12.28997, -22.046726, 10.253973, -12.575773, -22.046726, 10.418987, -12.004166, -19.902678, 10.088959, -12.28997, -19.456135, 10.253971, -12.124956, -19.902678, 9.968168, -12.124956, -19.902678, 9.968168, -12.28997, -19.456135, 10.253971, -12.28997, -19.902678, 9.923944, -11.959942, -19.902678, 10.253973, -12.28997, -19.456135, 10.253971, -12.004166, -19.902678, 10.088959, -12.28997, -19.902678, 9.923944, -12.28997, -19.456135, 10.253971, -12.454985, -19.902678, 9.968168, -12.004166, -19.902678, 10.418985, -12.28997, -19.456135, 10.253971, -11.959942, -19.902678, 10.253973, -12.454985, -19.902678, 9.968168, -12.28997, -19.456135, 10.253971, -12.575773, -19.902678, 10.088959, -12.124956, -19.902678, 10.539776, -12.28997, -19.456135, 10.253971, -12.004166, -19.902678, 10.418985, -12.575773, -19.902678, 10.088959, -12.28997, -19.456135, 10.253971, -12.619997, -19.902678, 10.253973, -12.28997, -19.902678, 10.584, -12.28997, -19.456135, 10.253971, -12.124956, -19.902678, 10.539776, -12.619997, -19.902678, 10.253973, -12.28997, -19.456135, 10.253971, -12.575773, -19.902678, 10.418985, -12.454985, -19.902678, 10.539776, -12.28997, -19.456135, 10.253971, -12.28997, -19.902678, 10.584, -12.575773, -19.902678, 10.418985, -12.28997, -19.456135, 10.253971, -12.454985, -19.902678, 10.539776, -12.004166, -22.046726, 6.859756, -12.124956, -19.902678, 6.7389646, -12.124956, -22.046726, 6.7389655, -12.004166, -22.046726, 6.859756, -12.004166, -19.902678, 6.859755, -12.124956, -19.902678, 6.7389646, -12.124956, -22.046726, 6.7389655, -12.28997, -19.902678, 6.6947412, -12.28997, -22.046726, 6.6947417, -12.124956, -22.046726, 6.7389655, -12.124956, -19.902678, 6.7389646, -12.28997, -19.902678, 6.6947412, -12.28997, -22.046726, 6.6947417, -12.454985, -19.902678, 6.7389646, -12.454985, -22.046726, 6.7389655, -12.28997, -22.046726, 6.6947417, -12.28997, -19.902678, 6.6947412, -12.454985, -19.902678, 6.7389646, -12.454985, -22.046726, 6.7389655, -12.575773, -19.902678, 6.859755, -12.575773, -22.046726, 6.859756, -12.454985, -22.046726, 6.7389655, -12.454985, -19.902678, 6.7389646, -12.575773, -19.902678, 6.859755, -12.575773, -22.046726, 6.859756, -12.619997, -19.902678, 7.024769, -12.619997, -22.046726, 7.02477, -12.575773, -22.046726, 6.859756, -12.575773, -19.902678, 6.859755, -12.619997, -19.902678, 7.024769, -12.619997, -22.046726, 7.02477, -12.575773, -19.902678, 7.1897826, -12.575773, -22.046726, 7.1897836, -12.619997, -22.046726, 7.02477, -12.619997, -19.902678, 7.024769, -12.575773, -19.902678, 7.1897826, -12.575773, -22.046726, 7.1897836, -12.454985, -19.902678, 7.310573, -12.454985, -22.046726, 7.3105736, -12.575773, -22.046726, 7.1897836, -12.575773, -19.902678, 7.1897826, -12.454985, -19.902678, 7.310573, -12.454985, -22.046726, 7.3105736, -12.28997, -19.902678, 7.354797, -12.28997, -22.046726, 7.3547974, -12.454985, -22.046726, 7.3105736, -12.454985, -19.902678, 7.310573, -12.28997, -19.902678, 7.354797, -12.28997, -22.046726, 7.3547974, -12.124956, -19.902678, 7.310573, -12.124956, -22.046726, 7.3105736, -12.28997, -22.046726, 7.3547974, -12.28997, -19.902678, 7.354797, -12.124956, -19.902678, 7.310573, -12.124956, -22.046726, 7.3105736, -12.004166, -19.902678, 7.1897826, -12.004166, -22.046726, 7.1897836, -12.124956, -22.046726, 7.3105736, -12.124956, -19.902678, 7.310573, -12.004166, -19.902678, 7.1897826, -12.004166, -22.046726, 7.1897836, -11.959942, -19.902678, 7.024769, -11.959942, -22.046726, 7.02477, -12.004166, -22.046726, 7.1897836, -12.004166, -19.902678, 7.1897826, -11.959942, -19.902678, 7.024769, -11.959942, -22.046726, 7.02477, -12.004166, -19.902678, 6.859755, -12.004166, -22.046726, 6.859756, -11.959942, -22.046726, 7.02477, -11.959942, -19.902678, 7.024769, -12.004166, -19.902678, 6.859755, -12.124956, -22.046726, 6.7389655, -12.28997, -22.046726, 7.02477, -12.004166, -22.046726, 6.859756, -12.004166, -22.046726, 6.859756, -12.28997, -22.046726, 7.02477, -11.959942, -22.046726, 7.02477, -12.28997, -22.046726, 6.6947417, -12.28997, -22.046726, 7.02477, -12.124956, -22.046726, 6.7389655, -11.959942, -22.046726, 7.02477, -12.28997, -22.046726, 7.02477, -12.004166, -22.046726, 7.1897836, -12.454985, -22.046726, 6.7389655, -12.28997, -22.046726, 7.02477, -12.28997, -22.046726, 6.6947417, -12.004166, -22.046726, 7.1897836, -12.28997, -22.046726, 7.02477, -12.124956, -22.046726, 7.3105736, -12.575773, -22.046726, 6.859756, -12.28997, -22.046726, 7.02477, -12.454985, -22.046726, 6.7389655, -12.124956, -22.046726, 7.3105736, -12.28997, -22.046726, 7.02477, -12.28997, -22.046726, 7.3547974, -12.619997, -22.046726, 7.02477, -12.28997, -22.046726, 7.02477, -12.575773, -22.046726, 6.859756, -12.28997, -22.046726, 7.3547974, -12.28997, -22.046726, 7.02477, -12.454985, -22.046726, 7.3105736, -12.575773, -22.046726, 7.1897836, -12.28997, -22.046726, 7.02477, -12.619997, -22.046726, 7.02477, -12.454985, -22.046726, 7.3105736, -12.28997, -22.046726, 7.02477, -12.575773, -22.046726, 7.1897836, -12.004166, -19.902678, 6.859755, -12.28997, -19.456135, 7.024769, -12.124956, -19.902678, 6.7389646, -12.124956, -19.902678, 6.7389646, -12.28997, -19.456135, 7.024769, -12.28997, -19.902678, 6.6947412, -11.959942, -19.902678, 7.024769, -12.28997, -19.456135, 7.024769, -12.004166, -19.902678, 6.859755, -12.28997, -19.902678, 6.6947412, -12.28997, -19.456135, 7.024769, -12.454985, -19.902678, 6.7389646, -12.004166, -19.902678, 7.1897826, -12.28997, -19.456135, 7.024769, -11.959942, -19.902678, 7.024769, -12.454985, -19.902678, 6.7389646, -12.28997, -19.456135, 7.024769, -12.575773, -19.902678, 6.859755, -12.124956, -19.902678, 7.310573, -12.28997, -19.456135, 7.024769, -12.004166, -19.902678, 7.1897826, -12.575773, -19.902678, 6.859755, -12.28997, -19.456135, 7.024769, -12.619997, -19.902678, 7.024769, -12.28997, -19.902678, 7.354797, -12.28997, -19.456135, 7.024769, -12.124956, -19.902678, 7.310573, -12.619997, -19.902678, 7.024769, -12.28997, -19.456135, 7.024769, -12.575773, -19.902678, 7.1897826, -12.454985, -19.902678, 7.310573, -12.28997, -19.456135, 7.024769, -12.28997, -19.902678, 7.354797, -12.575773, -19.902678, 7.1897826, -12.28997, -19.456135, 7.024769, -12.454985, -19.902678, 7.310573, -12.917406, -22.143438, 6.933488, -12.989674, -20.442587, 6.8666687, -12.989674, -22.143438, 6.8666697, -12.917406, -22.143438, 6.933488, -12.917406, -20.442587, 6.933487, -12.989674, -20.442587, 6.8666687, -12.989674, -22.143438, 6.8666697, -13.088398, -20.442587, 6.8422055, -13.088398, -22.143438, 6.842206, -12.989674, -22.143438, 6.8666697, -12.989674, -20.442587, 6.8666687, -13.088398, -20.442587, 6.8422055, -13.088398, -22.143438, 6.842206, -13.187122, -20.442587, 6.8666687, -13.187122, -22.143438, 6.8666697, -13.088398, -22.143438, 6.842206, -13.088398, -20.442587, 6.8422055, -13.187122, -20.442587, 6.8666687, -13.187122, -22.143438, 6.8666697, -13.259388, -20.442587, 6.933487, -13.259388, -22.143438, 6.933488, -13.187122, -22.143438, 6.8666697, -13.187122, -20.442587, 6.8666687, -13.259388, -20.442587, 6.933487, -13.259388, -22.143438, 6.933488, -13.285847, -20.442587, 7.024769, -13.285847, -22.143438, 7.02477, -13.259388, -22.143438, 6.933488, -13.259388, -20.442587, 6.933487, -13.285847, -20.442587, 7.024769, -13.285847, -22.143438, 7.02477, -13.259388, -20.442587, 7.1160507, -13.259388, -22.143438, 7.116051, -13.285847, -22.143438, 7.02477, -13.285847, -20.442587, 7.024769, -13.259388, -20.442587, 7.1160507, -13.259388, -22.143438, 7.116051, -13.187122, -20.442587, 7.182869, -13.187122, -22.143438, 7.18287, -13.259388, -22.143438, 7.116051, -13.259388, -20.442587, 7.1160507, -13.187122, -20.442587, 7.182869, -13.187122, -22.143438, 7.18287, -13.088398, -20.442587, 7.2073326, -13.088398, -22.143438, 7.207333, -13.187122, -22.143438, 7.18287, -13.187122, -20.442587, 7.182869, -13.088398, -20.442587, 7.2073326, -13.088398, -22.143438, 7.207333, -12.989674, -20.442587, 7.182869, -12.989674, -22.143438, 7.18287, -13.088398, -22.143438, 7.207333, -13.088398, -20.442587, 7.2073326, -12.989674, -20.442587, 7.182869, -12.989674, -22.143438, 7.18287, -12.917406, -20.442587, 7.1160507, -12.917406, -22.143438, 7.116051, -12.989674, -22.143438, 7.18287, -12.989674, -20.442587, 7.182869, -12.917406, -20.442587, 7.1160507, -12.917406, -22.143438, 7.116051, -12.890947, -20.442587, 7.024769, -12.890947, -22.143438, 7.02477, -12.917406, -22.143438, 7.116051, -12.917406, -20.442587, 7.1160507, -12.890947, -20.442587, 7.024769, -12.890947, -22.143438, 7.02477, -12.917406, -20.442587, 6.933487, -12.917406, -22.143438, 6.933488, -12.890947, -22.143438, 7.02477, -12.890947, -20.442587, 7.024769, -12.917406, -20.442587, 6.933487, -12.989674, -22.143438, 6.8666697, -13.088398, -22.143438, 7.02477, -12.917406, -22.143438, 6.933488, -12.917406, -22.143438, 6.933488, -13.088398, -22.143438, 7.02477, -12.890947, -22.143438, 7.02477, -13.088398, -22.143438, 6.842206, -13.088398, -22.143438, 7.02477, -12.989674, -22.143438, 6.8666697, -12.890947, -22.143438, 7.02477, -13.088398, -22.143438, 7.02477, -12.917406, -22.143438, 7.116051, -13.187122, -22.143438, 6.8666697, -13.088398, -22.143438, 7.02477, -13.088398, -22.143438, 6.842206, -12.917406, -22.143438, 7.116051, -13.088398, -22.143438, 7.02477, -12.989674, -22.143438, 7.18287, -13.259388, -22.143438, 6.933488, -13.088398, -22.143438, 7.02477, -13.187122, -22.143438, 6.8666697, -12.989674, -22.143438, 7.18287, -13.088398, -22.143438, 7.02477, -13.088398, -22.143438, 7.207333, -13.285847, -22.143438, 7.02477, -13.088398, -22.143438, 7.02477, -13.259388, -22.143438, 6.933488, -13.088398, -22.143438, 7.207333, -13.088398, -22.143438, 7.02477, -13.187122, -22.143438, 7.18287, -13.259388, -22.143438, 7.116051, -13.088398, -22.143438, 7.02477, -13.285847, -22.143438, 7.02477, -13.187122, -22.143438, 7.18287, -13.088398, -22.143438, 7.02477, -13.259388, -22.143438, 7.116051, -12.917406, -20.442587, 6.933487, -13.088398, -20.08835, 7.024769, -12.989674, -20.442587, 6.8666687, -12.989674, -20.442587, 6.8666687, -13.088398, -20.08835, 7.024769, -13.088398, -20.442587, 6.8422055, -12.890947, -20.442587, 7.024769, -13.088398, -20.08835, 7.024769, -12.917406, -20.442587, 6.933487, -13.088398, -20.442587, 6.8422055, -13.088398, -20.08835, 7.024769, -13.187122, -20.442587, 6.8666687, -12.917406, -20.442587, 7.1160507, -13.088398, -20.08835, 7.024769, -12.890947, -20.442587, 7.024769, -13.187122, -20.442587, 6.8666687, -13.088398, -20.08835, 7.024769, -13.259388, -20.442587, 6.933487, -12.989674, -20.442587, 7.182869, -13.088398, -20.08835, 7.024769, -12.917406, -20.442587, 7.1160507, -13.259388, -20.442587, 6.933487, -13.088398, -20.08835, 7.024769, -13.285847, -20.442587, 7.024769, -13.088398, -20.442587, 7.2073326, -13.088398, -20.08835, 7.024769, -12.989674, -20.442587, 7.182869, -13.285847, -20.442587, 7.024769, -13.088398, -20.08835, 7.024769, -13.259388, -20.442587, 7.1160507, -13.187122, -20.442587, 7.182869, -13.088398, -20.08835, 7.024769, -13.088398, -20.442587, 7.2073326, -13.259388, -20.442587, 7.1160507, -13.088398, -20.08835, 7.024769, -13.187122, -20.442587, 7.182869, 7.7560043, -21.843687, -1.5963855, 7.8594513, -21.712654, -1.5136316, 7.8594513, -21.889545, -1.5601864, 7.7560043, -21.843687, -1.5963855, 7.7198067, -21.712654, -1.5601864, 7.8594513, -21.712654, -1.5136316, 7.7198067, -21.889545, -1.6998296, 7.7560043, -21.843687, -1.5963855, 7.8594513, -21.889545, -1.5601864, 7.7198067, -21.889545, -1.6998296, 7.7198067, -21.712654, -1.5601864, 7.7560043, -21.843687, -1.5963855, 7.7198067, -21.712654, -1.5601864, 7.8594513, -21.392513, -1.5493045, 7.8594513, -21.712654, -1.5136316, 7.8594513, -21.712654, -1.5136316, 7.8594513, -21.392513, -1.5493045, 7.9628983, -21.392513, -1.5855265, 7.7198067, -21.712654, -1.5601864, 7.7560043, -21.392513, -1.5855265, 7.8594513, -21.392513, -1.5493045, 7.8594513, -21.712654, -1.5136316, 7.9628983, -21.392513, -1.5855265, 7.999096, -21.712654, -1.5601864, 7.8594513, -21.889545, -1.5601864, 7.8594513, -21.712654, -1.5136316, 7.999096, -21.712654, -1.5601864, 7.999096, -21.712654, -1.5601864, 7.9628983, -21.392513, -1.5855265, 7.999096, -21.392513, -1.6889691, 7.673259, -21.712656, -1.6998296, 7.7560043, -21.392513, -1.5855265, 7.7198067, -21.712654, -1.5601864, 7.7198067, -21.889545, -1.6998296, 7.673259, -21.712656, -1.6998296, 7.7198067, -21.712654, -1.5601864, 7.673259, -21.712656, -1.6998296, 7.7198067, -21.392513, -1.6889691, 7.7560043, -21.392513, -1.5855265, 7.999096, -21.712654, -1.5601864, 7.999096, -21.392513, -1.6889691, 8.045644, -21.712656, -1.6998296, 8.045644, -21.712656, -1.6998296, 7.999096, -21.392513, -1.6889691, 7.9628983, -21.392513, -1.7924135, 7.8594513, -21.889545, -1.5601864, 7.999096, -21.712654, -1.5601864, 7.9628983, -21.843687, -1.5963855, 7.9628983, -21.843687, -1.5963855, 7.999096, -21.712654, -1.5601864, 8.045644, -21.712656, -1.6998296, 7.7198067, -21.889545, -1.6998296, 7.8594513, -21.889545, -1.5601864, 7.8594513, -21.948505, -1.6998296, 7.8594513, -21.948505, -1.6998296, 7.8594513, -21.889545, -1.5601864, 7.9628983, -21.843687, -1.5963855, 7.7560043, -21.843687, -1.8032722, 7.673259, -21.712656, -1.6998296, 7.7198067, -21.889545, -1.6998296, 7.7560043, -21.843687, -1.8032722, 7.7198067, -21.889545, -1.6998296, 7.8594513, -21.948505, -1.6998296, 7.7198067, -21.712656, -1.8394728, 7.7198067, -21.392513, -1.6889691, 7.673259, -21.712656, -1.6998296, 7.7560043, -21.843687, -1.8032722, 7.7198067, -21.712656, -1.8394728, 7.673259, -21.712656, -1.6998296, 7.7198067, -21.712656, -1.8394728, 7.7560043, -21.392513, -1.7924135, 7.7198067, -21.392513, -1.6889691, 7.7560043, -21.843687, -1.8032722, 7.8594513, -21.948505, -1.6998296, 7.8594513, -21.889545, -1.8394728, 7.7198067, -21.712656, -1.8394728, 7.7560043, -21.843687, -1.8032722, 7.8594513, -21.889545, -1.8394728, 7.8594513, -21.948505, -1.6998296, 7.9628983, -21.843687, -1.5963855, 7.999096, -21.889545, -1.6998296, 7.8594513, -21.889545, -1.8394728, 7.8594513, -21.948505, -1.6998296, 7.999096, -21.889545, -1.6998296, 7.9628983, -21.843687, -1.5963855, 8.045644, -21.712656, -1.6998296, 7.999096, -21.889545, -1.6998296, 7.8594513, -21.889545, -1.8394728, 7.999096, -21.889545, -1.6998296, 7.9628983, -21.843687, -1.8032722, 7.7560043, -21.392513, -1.7924135, 7.7198067, -21.712656, -1.8394728, 7.8594513, -21.712656, -1.8860278, 7.7198067, -21.712656, -1.8394728, 7.8594513, -21.889545, -1.8394728, 7.8594513, -21.712656, -1.8860278, 7.8594513, -21.712656, -1.8860278, 7.8594513, -21.889545, -1.8394728, 7.9628983, -21.843687, -1.8032722, 7.7560043, -21.392513, -1.7924135, 7.8594513, -21.712656, -1.8860278, 7.8594513, -21.392513, -1.8286123, 7.999096, -21.889545, -1.6998296, 8.045644, -21.712656, -1.6998296, 7.999096, -21.712656, -1.8394728, 7.999096, -21.889545, -1.6998296, 7.999096, -21.712656, -1.8394728, 7.9628983, -21.843687, -1.8032722, 7.8594513, -21.712656, -1.8860278, 7.9628983, -21.843687, -1.8032722, 7.999096, -21.712656, -1.8394728, 7.8594513, -21.392513, -1.8286123, 7.8594513, -21.712656, -1.8860278, 7.999096, -21.712656, -1.8394728, 8.045644, -21.712656, -1.6998296, 7.9628983, -21.392513, -1.7924135, 7.999096, -21.712656, -1.8394728, 7.8594513, -21.392513, -1.8286123, 7.999096, -21.712656, -1.8394728, 7.9628983, -21.392513, -1.7924135, 7.597028, -21.442598, -1.34617, 7.8594513, -21.312298, -1.1490684, 7.8594513, -21.49598, -1.2661133, 7.597028, -21.442598, -1.34617, 7.505172, -21.314877, -1.2550125, 7.8594513, -21.312298, -1.1490684, 7.8594513, -21.49598, -1.2661133, 7.8594513, -21.312298, -1.1490684, 8.213731, -21.314877, -1.2550125, 7.505172, -21.409956, -1.6840334, 7.505172, -21.314877, -1.2550125, 7.597028, -21.442598, -1.34617, 7.505172, -21.409956, -1.6840334, 7.597028, -21.442598, -1.34617, 7.8594513, -21.49598, -1.2661133, 7.505172, -21.409956, -1.6840334, 7.387093, -21.22724, -1.6845381, 7.505172, -21.314877, -1.2550125, 7.8594513, -21.49598, -1.2661133, 8.213731, -21.314877, -1.2550125, 8.121874, -21.442598, -1.34617, 8.121874, -21.442598, -1.34617, 8.213731, -21.314877, -1.2550125, 8.331823, -21.22724, -1.6845381, 7.505172, -21.409956, -1.6840334, 7.8594513, -21.49598, -1.2661133, 7.8594513, -21.492252, -1.6844721, 7.8594513, -21.492252, -1.6844721, 7.8594513, -21.49598, -1.2661133, 8.121874, -21.442598, -1.34617, 7.597028, -21.339413, -1.9443204, 7.387093, -21.22724, -1.6845381, 7.505172, -21.409956, -1.6840334, 7.597028, -21.339413, -1.9443204, 7.505172, -21.409956, -1.6840334, 7.8594513, -21.492252, -1.6844721, 7.597028, -21.339413, -1.9443204, 7.505172, -21.222763, -2.0274482, 7.387093, -21.22724, -1.6845381, 8.121874, -21.442598, -1.34617, 8.331823, -21.22724, -1.6845381, 8.213731, -21.409956, -1.6840334, 7.8594513, -21.492252, -1.6844721, 8.121874, -21.442598, -1.34617, 8.213731, -21.409956, -1.6840334, 8.213731, -21.409956, -1.6840334, 8.331823, -21.22724, -1.6845381, 8.213731, -21.222763, -2.0274482, 7.597028, -21.339413, -1.9443204, 7.8594513, -21.492252, -1.6844721, 7.8594513, -21.4032, -2.0336347, 7.505172, -21.222763, -2.0274482, 7.597028, -21.339413, -1.9443204, 7.8594513, -21.4032, -2.0336347, 7.8594513, -21.4032, -2.0336347, 7.8594513, -21.492252, -1.6844721, 8.213731, -21.409956, -1.6840334, 7.505172, -21.222763, -2.0274482, 7.8594513, -21.4032, -2.0336347, 7.8594513, -21.227156, -2.1436162, 8.213731, -21.409956, -1.6840334, 8.213731, -21.222763, -2.0274482, 8.121874, -21.339413, -1.9443204, 7.8594513, -21.4032, -2.0336347, 8.213731, -21.409956, -1.6840334, 8.121874, -21.339413, -1.9443204, 7.8594513, -21.227156, -2.1436162, 8.121874, -21.339413, -1.9443204, 8.213731, -21.222763, -2.0274482, 7.8594513, -21.227156, -2.1436162, 7.8594513, -21.4032, -2.0336347, 8.121874, -21.339413, -1.9443204, 7.8594513, -21.312298, -1.1490684, 8.121874, -21.111137, -1.4137647, 8.213731, -21.314877, -1.2550125, 8.213731, -21.314877, -1.2550125, 8.121874, -21.111137, -1.4137647, 8.213731, -21.024748, -1.6911418, 7.8594513, -21.312298, -1.1490684, 7.8594513, -21.038862, -1.3319969, 8.121874, -21.111137, -1.4137647, 7.8594513, -21.038862, -1.3319969, 8.213731, -21.024748, -1.6911418, 8.121874, -21.111137, -1.4137647, 8.213731, -21.314877, -1.2550125, 8.213731, -21.024748, -1.6911418, 8.331823, -21.22724, -1.6845381, 7.505172, -21.314877, -1.2550125, 7.8594513, -21.038862, -1.3319969, 7.8594513, -21.312298, -1.1490684, 7.505172, -21.314877, -1.2550125, 7.597028, -21.111137, -1.4137647, 7.8594513, -21.038862, -1.3319969, 7.387093, -21.22724, -1.6845381, 7.597028, -21.111137, -1.4137647, 7.505172, -21.314877, -1.2550125, 7.8594513, -21.038862, -1.3319969, 7.8594513, -20.941704, -1.6914926, 8.213731, -21.024748, -1.6911418, 7.597028, -21.111137, -1.4137647, 7.8594513, -20.941704, -1.6914926, 7.8594513, -21.038862, -1.3319969, 8.331823, -21.22724, -1.6845381, 8.213731, -21.024748, -1.6911418, 8.121874, -21.092463, -1.9232578, 7.8594513, -20.941704, -1.6914926, 8.121874, -21.092463, -1.9232578, 8.213731, -21.024748, -1.6911418, 8.331823, -21.22724, -1.6845381, 8.121874, -21.092463, -1.9232578, 8.213731, -21.222763, -2.0274482, 7.387093, -21.22724, -1.6845381, 7.505172, -21.024748, -1.6911418, 7.597028, -21.111137, -1.4137647, 7.597028, -21.111137, -1.4137647, 7.505172, -21.024748, -1.6911418, 7.8594513, -20.941704, -1.6914926, 7.505172, -21.222763, -2.0274482, 7.505172, -21.024748, -1.6911418, 7.387093, -21.22724, -1.6845381, 7.8594513, -21.02841, -2.012529, 8.213731, -21.222763, -2.0274482, 8.121874, -21.092463, -1.9232578, 7.8594513, -20.941704, -1.6914926, 7.8594513, -21.02841, -2.012529, 8.121874, -21.092463, -1.9232578, 7.505172, -21.024748, -1.6911418, 7.8594513, -21.02841, -2.012529, 7.8594513, -20.941704, -1.6914926, 7.8594513, -21.02841, -2.012529, 7.8594513, -21.227156, -2.1436162, 8.213731, -21.222763, -2.0274482, 7.505172, -21.222763, -2.0274482, 7.597028, -21.092463, -1.9232578, 7.505172, -21.024748, -1.6911418, 7.505172, -21.024748, -1.6911418, 7.597028, -21.092463, -1.9232578, 7.8594513, -21.02841, -2.012529, 7.597028, -21.092463, -1.9232578, 7.505172, -21.222763, -2.0274482, 7.8594513, -21.227156, -2.1436162, 7.597028, -21.092463, -1.9232578, 7.8594513, -21.227156, -2.1436162, 7.8594513, -21.02841, -2.012529, 7.7984443, -21.95358, -2.1591706, 7.8594513, -21.864279, -2.1397543, 7.8594513, -21.971691, -2.1298366, 7.7984443, -21.95358, -2.1591706, 7.7770944, -21.873667, -2.165554, 7.8594513, -21.864279, -2.1397543, 7.7770944, -21.999872, -2.2072396, 7.7984443, -21.95358, -2.1591706, 7.8594513, -21.971691, -2.1298366, 7.7770944, -21.999872, -2.2072396, 7.7770944, -21.873667, -2.165554, 7.7984443, -21.95358, -2.1591706, 7.7770944, -21.873667, -2.165554, 7.8594513, -21.694061, -2.224133, 7.8594513, -21.864279, -2.1397543, 7.8594513, -21.864279, -2.1397543, 7.8594513, -21.694061, -2.224133, 7.9204583, -21.701368, -2.2442064, 7.7770944, -21.873667, -2.165554, 7.7984443, -21.701368, -2.2442064, 7.8594513, -21.694061, -2.224133, 7.8594513, -21.864279, -2.1397543, 7.9204583, -21.701368, -2.2442064, 7.941808, -21.873667, -2.165554, 7.8594513, -21.971691, -2.1298366, 7.8594513, -21.864279, -2.1397543, 7.941808, -21.873667, -2.165554, 7.941808, -21.873667, -2.165554, 7.9204583, -21.701368, -2.2442064, 7.941808, -21.722242, -2.3015342, 7.7496324, -21.901846, -2.2429338, 7.7984443, -21.701368, -2.2442064, 7.7770944, -21.873667, -2.165554, 7.7770944, -21.999872, -2.2072396, 7.7496324, -21.901846, -2.2429338, 7.7770944, -21.873667, -2.165554, 7.7496324, -21.901846, -2.2429338, 7.7770944, -21.722242, -2.3015342, 7.7984443, -21.701368, -2.2442064, 7.941808, -21.873667, -2.165554, 7.941808, -21.722242, -2.3015342, 7.96927, -21.901846, -2.2429338, 7.96927, -21.901846, -2.2429338, 7.941808, -21.722242, -2.3015342, 7.9204583, -21.743116, -2.3588605, 7.8594513, -21.971691, -2.1298366, 7.941808, -21.873667, -2.165554, 7.9204583, -21.95358, -2.1591706, 7.9204583, -21.95358, -2.1591706, 7.941808, -21.873667, -2.165554, 7.96927, -21.901846, -2.2429338, 7.7770944, -21.999872, -2.2072396, 7.8594513, -21.971691, -2.1298366, 7.8594513, -22.032564, -2.1953483, 7.8594513, -22.032564, -2.1953483, 7.8594513, -21.971691, -2.1298366, 7.9204583, -21.95358, -2.1591706, 7.7984443, -21.995344, -2.2738247, 7.7496324, -21.901846, -2.2429338, 7.7770944, -21.999872, -2.2072396, 7.7984443, -21.995344, -2.2738247, 7.7770944, -21.999872, -2.2072396, 7.8594513, -22.032564, -2.1953483, 7.7770944, -21.930044, -2.320335, 7.7770944, -21.722242, -2.3015342, 7.7496324, -21.901846, -2.2429338, 7.7984443, -21.995344, -2.2738247, 7.7770944, -21.930044, -2.320335, 7.7496324, -21.901846, -2.2429338, 7.7770944, -21.930044, -2.320335, 7.7984443, -21.743116, -2.3588605, 7.7770944, -21.722242, -2.3015342, 7.7984443, -21.995344, -2.2738247, 7.8594513, -22.032564, -2.1953483, 7.8594513, -22.02807, -2.2846193, 7.7770944, -21.930044, -2.320335, 7.7984443, -21.995344, -2.2738247, 7.8594513, -22.02807, -2.2846193, 7.8594513, -22.032564, -2.1953483, 7.9204583, -21.95358, -2.1591706, 7.941808, -21.999872, -2.2072396, 7.8594513, -22.02807, -2.2846193, 7.8594513, -22.032564, -2.1953483, 7.941808, -21.999872, -2.2072396, 7.9204583, -21.95358, -2.1591706, 7.96927, -21.901846, -2.2429338, 7.941808, -21.999872, -2.2072396, 7.8594513, -22.02807, -2.2846193, 7.941808, -21.999872, -2.2072396, 7.9204583, -21.995344, -2.2738247, 7.7984443, -21.743116, -2.3588605, 7.7770944, -21.930044, -2.320335, 7.8594513, -21.939434, -2.3461366, 7.7770944, -21.930044, -2.320335, 7.8594513, -22.02807, -2.2846193, 7.8594513, -21.939434, -2.3461366, 7.8594513, -21.939434, -2.3461366, 7.8594513, -22.02807, -2.2846193, 7.9204583, -21.995344, -2.2738247, 7.7984443, -21.743116, -2.3588605, 7.8594513, -21.939434, -2.3461366, 7.8594513, -21.750423, -2.3789139, 7.941808, -21.999872, -2.2072396, 7.96927, -21.901846, -2.2429338, 7.941808, -21.930044, -2.320335, 7.941808, -21.999872, -2.2072396, 7.941808, -21.930044, -2.320335, 7.9204583, -21.995344, -2.2738247, 7.8594513, -21.939434, -2.3461366, 7.9204583, -21.995344, -2.2738247, 7.941808, -21.930044, -2.320335, 7.8594513, -21.750423, -2.3789139, 7.8594513, -21.939434, -2.3461366, 7.941808, -21.930044, -2.320335, 7.96927, -21.901846, -2.2429338, 7.9204583, -21.743116, -2.3588605, 7.941808, -21.930044, -2.320335, 7.8594513, -21.750423, -2.3789139, 7.941808, -21.930044, -2.320335, 7.9204583, -21.743116, -2.3588605, 7.7046704, -21.680813, -2.1014476, 7.8594513, -21.56882, -2.0185182, 7.8594513, -21.694244, -2.0463152, 7.7046704, -21.680813, -2.1014476, 7.6505113, -21.591642, -2.0767002, 7.8594513, -21.56882, -2.0185182, 7.8594513, -21.694244, -2.0463152, 7.8594513, -21.56882, -2.0185182, 8.068392, -21.591642, -2.0767002, 7.6505113, -21.730915, -2.2952814, 7.6505113, -21.591642, -2.0767002, 7.7046704, -21.680813, -2.1014476, 7.6505113, -21.730915, -2.2952814, 7.7046704, -21.680813, -2.1014476, 7.8594513, -21.694244, -2.0463152, 7.6505113, -21.730915, -2.2952814, 7.5808535, -21.62976, -2.3324466, 7.6505113, -21.591642, -2.0767002, 7.8594513, -21.694244, -2.0463152, 8.068392, -21.591642, -2.0767002, 8.014233, -21.680813, -2.1014476, 8.014233, -21.680813, -2.1014476, 8.068392, -21.591642, -2.0767002, 8.138048, -21.62976, -2.3324466, 7.6505113, -21.730915, -2.2952814, 7.8594513, -21.694244, -2.0463152, 7.8594513, -21.776608, -2.278893, 7.8594513, -21.776608, -2.278893, 7.8594513, -21.694244, -2.0463152, 8.014233, -21.680813, -2.1014476, 7.7046704, -21.744349, -2.4537702, 7.5808535, -21.62976, -2.3324466, 7.6505113, -21.730915, -2.2952814, 7.7046704, -21.744349, -2.4537702, 7.6505113, -21.730915, -2.2952814, 7.8594513, -21.776608, -2.278893, 7.7046704, -21.744349, -2.4537702, 7.6505113, -21.696476, -2.5233836, 7.5808535, -21.62976, -2.3324466, 8.014233, -21.680813, -2.1014476, 8.138048, -21.62976, -2.3324466, 8.068392, -21.730915, -2.2952814, 7.8594513, -21.776608, -2.278893, 8.014233, -21.680813, -2.1014476, 8.068392, -21.730915, -2.2952814, 8.068392, -21.730915, -2.2952814, 8.138048, -21.62976, -2.3324466, 8.068392, -21.696476, -2.5233836, 7.7046704, -21.744349, -2.4537702, 7.8594513, -21.776608, -2.278893, 7.8594513, -21.79773, -2.490386, 7.6505113, -21.696476, -2.5233836, 7.7046704, -21.744349, -2.4537702, 7.8594513, -21.79773, -2.490386, 7.8594513, -21.79773, -2.490386, 7.8594513, -21.776608, -2.278893, 8.068392, -21.730915, -2.2952814, 7.6505113, -21.696476, -2.5233836, 7.8594513, -21.79773, -2.490386, 7.8594513, -21.722359, -2.586853, 8.068392, -21.730915, -2.2952814, 8.068392, -21.696476, -2.5233836, 8.014233, -21.744349, -2.4537702, 7.8594513, -21.79773, -2.490386, 8.068392, -21.730915, -2.2952814, 8.014233, -21.744349, -2.4537702, 7.8594513, -21.722359, -2.586853, 8.014233, -21.744349, -2.4537702, 8.068392, -21.696476, -2.5233836, 7.8594513, -21.722359, -2.586853, 7.8594513, -21.79773, -2.490386, 8.014233, -21.744349, -2.4537702, 7.8594513, -21.56882, -2.0185182, 8.014233, -21.510761, -2.2058134, 8.068392, -21.591642, -2.0767002, 8.068392, -21.591642, -2.0767002, 8.014233, -21.510761, -2.2058134, 8.068392, -21.51887, -2.3769612, 7.8594513, -21.56882, -2.0185182, 7.8594513, -21.4542, -2.1750765, 8.014233, -21.510761, -2.2058134, 7.8594513, -21.4542, -2.1750765, 8.068392, -21.51887, -2.3769612, 8.014233, -21.510761, -2.2058134, 8.068392, -21.591642, -2.0767002, 8.068392, -21.51887, -2.3769612, 8.138048, -21.62976, -2.3324466, 7.6505113, -21.591642, -2.0767002, 7.8594513, -21.4542, -2.1750765, 7.8594513, -21.56882, -2.0185182, 7.6505113, -21.591642, -2.0767002, 7.7046704, -21.510761, -2.2058134, 7.8594513, -21.4542, -2.1750765, 7.5808535, -21.62976, -2.3324466, 7.7046704, -21.510761, -2.2058134, 7.6505113, -21.591642, -2.0767002, 7.8594513, -21.4542, -2.1750765, 7.8594513, -21.47291, -2.3939195, 8.068392, -21.51887, -2.3769612, 7.7046704, -21.510761, -2.2058134, 7.8594513, -21.47291, -2.3939195, 7.8594513, -21.4542, -2.1750765, 8.138048, -21.62976, -2.3324466, 8.068392, -21.51887, -2.3769612, 8.014233, -21.603245, -2.491922, 7.8594513, -21.47291, -2.3939195, 8.014233, -21.603245, -2.491922, 8.068392, -21.51887, -2.3769612, 8.138048, -21.62976, -2.3324466, 8.014233, -21.603245, -2.491922, 8.068392, -21.696476, -2.5233836, 7.5808535, -21.62976, -2.3324466, 7.6505113, -21.51887, -2.3769612, 7.7046704, -21.510761, -2.2058134, 7.7046704, -21.510761, -2.2058134, 7.6505113, -21.51887, -2.3769612, 7.8594513, -21.47291, -2.3939195, 7.6505113, -21.696476, -2.5233836, 7.6505113, -21.51887, -2.3769612, 7.5808535, -21.62976, -2.3324466, 7.8594513, -21.585766, -2.5543175, 8.068392, -21.696476, -2.5233836, 8.014233, -21.603245, -2.491922, 7.8594513, -21.47291, -2.3939195, 7.8594513, -21.585766, -2.5543175, 8.014233, -21.603245, -2.491922, 7.6505113, -21.51887, -2.3769612, 7.8594513, -21.585766, -2.5543175, 7.8594513, -21.47291, -2.3939195, 7.8594513, -21.585766, -2.5543175, 7.8594513, -21.722359, -2.586853, 8.068392, -21.696476, -2.5233836, 7.6505113, -21.696476, -2.5233836, 7.7046704, -21.603245, -2.491922, 7.6505113, -21.51887, -2.3769612, 7.6505113, -21.51887, -2.3769612, 7.7046704, -21.603245, -2.491922, 7.8594513, -21.585766, -2.5543175, 7.7046704, -21.603245, -2.491922, 7.6505113, -21.696476, -2.5233836, 7.8594513, -21.722359, -2.586853, 7.7046704, -21.603245, -2.491922, 7.8594513, -21.722359, -2.586853, 7.8594513, -21.585766, -2.5543175, -3.1935778, 3.8995895, -1.8595574, -2.9311557, 3.6870894, -1.6457057, -2.9311557, 3.8599355, -1.9618971, -3.1935778, 3.8995895, -1.8595574, -3.2854238, 3.7667134, -1.6248863, -2.9311557, 3.6870894, -1.6457057, -2.9311557, 3.8599355, -1.9618971, -2.9311557, 3.6870894, -1.6457057, -2.5768762, 3.7667134, -1.6248863, -2.9311557, 3.8599355, -1.9618971, -2.5768762, 3.7667134, -1.6248863, -2.6687322, 3.8995895, -1.8595574, -2.9311557, 3.6870894, -1.6457057, -2.6687322, 3.6513314, -1.305656, -2.5768762, 3.7667134, -1.6248863, -2.9311557, 3.6870894, -1.6457057, -2.9311557, 3.579767, -1.2413225, -2.6687322, 3.6513314, -1.305656, -3.2854238, 3.7667134, -1.6248863, -2.9311557, 3.579767, -1.2413225, -2.9311557, 3.6870894, -1.6457057, -3.2854238, 3.7667134, -1.6248863, -3.1935778, 3.6513314, -1.305656, -2.9311557, 3.579767, -1.2413225, -3.0859365, 3.6335888, -2.4534812, -2.9311557, 3.5589857, -2.2920804, -2.9311557, 3.5909512, -2.5021868, -3.0859365, 3.6335888, -2.4534812, -3.1401029, 3.6073158, -2.2966237, -2.9311557, 3.5589857, -2.2920804, -2.9311557, 3.5909512, -2.5021868, -2.9311557, 3.5589857, -2.2920804, -2.722207, 3.6073158, -2.2966237, -2.9311557, 3.5909512, -2.5021868, -2.722207, 3.6073158, -2.2966237, -2.7763748, 3.6335888, -2.4534812, -2.9311557, 3.5589857, -2.2920804, -2.7763748, 3.6077993, -2.096417, -2.722207, 3.6073158, -2.2966237, -2.9311557, 3.5589857, -2.2920804, -2.9311557, 3.5811186, -2.046321, -2.7763748, 3.6077993, -2.096417, -3.1401029, 3.6073158, -2.2966237, -2.9311557, 3.5811186, -2.046321, -2.9311557, 3.5589857, -2.2920804, -3.1401029, 3.6073158, -2.2966237, -3.0859365, 3.6077993, -2.096417, -2.9311557, 3.5811186, -2.046321, -3.1935806, 3.6513338, -1.3056598, -2.931157, 3.7287045, -1.0824075, -2.931157, 3.5797653, -1.2413273, -2.931157, 3.5797653, -1.2413273, -2.931157, 3.7287045, -1.0824075, -2.5768776, 3.7524617, -1.1856833, -3.1935806, 3.6513338, -1.3056598, -3.2854366, 3.7524617, -1.1856833, -2.931157, 3.7287045, -1.0824075, -2.931157, 3.5797653, -1.2413273, -2.5768776, 3.7524617, -1.1856833, -2.668735, 3.6513338, -1.3056598, -3.2854366, 3.766717, -1.6248941, -3.2854366, 3.7524617, -1.1856833, -3.1935806, 3.6513338, -1.3056598, -2.668735, 3.6513338, -1.3056598, -2.5768776, 3.7524617, -1.1856833, -2.4587846, 3.9438598, -1.5800798, -3.2854366, 3.766717, -1.6248941, -3.4035153, 3.9438598, -1.5800798, -3.2854366, 3.7524617, -1.1856833, -2.668735, 3.6513338, -1.3056598, -2.4587846, 3.9438598, -1.5800798, -2.5768776, 3.766717, -1.6248941, -3.1935806, 3.8995967, -1.8595555, -3.4035153, 3.9438598, -1.5800798, -3.2854366, 3.766717, -1.6248941, -2.5768776, 3.766717, -1.6248941, -2.4587846, 3.9438598, -1.5800798, -2.5768776, 4.0332108, -1.9111595, -3.1935806, 3.8995967, -1.8595555, -3.2854366, 4.0332108, -1.9111595, -3.4035153, 3.9438598, -1.5800798, -2.5768776, 3.766717, -1.6248941, -2.5768776, 4.0332108, -1.9111595, -2.668735, 3.8995967, -1.8595555, -3.2854366, 4.0332108, -1.9111595, -3.1935806, 3.8995967, -1.8595555, -2.931157, 3.8599317, -1.9618936, -2.931157, 4.0577416, -2.0247965, -2.668735, 3.8995967, -1.8595555, -2.5768776, 4.0332108, -1.9111595, -3.2854366, 4.0332108, -1.9111595, -2.931157, 3.8599317, -1.9618936, -2.931157, 4.0577416, -2.0247965, -2.931157, 4.0577416, -2.0247965, -2.931157, 3.8599317, -1.9618936, -2.668735, 3.8995967, -1.8595555, -2.931157, 3.7287045, -1.0824075, -2.668735, 3.9892077, -1.288969, -2.5768776, 3.7524617, -1.1856833, -2.5768776, 3.7524617, -1.1856833, -2.668735, 3.9892077, -1.288969, -2.5768776, 4.141674, -1.5362737, -2.931157, 3.7287045, -1.0824075, -2.931157, 4.0389485, -1.191823, -2.668735, 3.9892077, -1.288969, -2.931157, 4.0389485, -1.191823, -2.5768776, 4.141674, -1.5362737, -2.668735, 3.9892077, -1.288969, -2.5768776, 3.7524617, -1.1856833, -2.5768776, 4.141674, -1.5362737, -2.4587846, 3.9438598, -1.5800798, -3.2854366, 3.7524617, -1.1856833, -2.931157, 4.0389485, -1.191823, -2.931157, 3.7287045, -1.0824075, -3.2854366, 3.7524617, -1.1856833, -3.1935806, 3.9892077, -1.288969, -2.931157, 4.0389485, -1.191823, -3.4035153, 3.9438598, -1.5800798, -3.1935806, 3.9892077, -1.288969, -3.2854366, 3.7524617, -1.1856833, -2.931157, 4.0389485, -1.191823, -2.931157, 4.2222066, -1.5160246, -2.5768776, 4.141674, -1.5362737, -3.1935806, 3.9892077, -1.288969, -2.931157, 4.2222066, -1.5160246, -2.931157, 4.0389485, -1.191823, -2.4587846, 3.9438598, -1.5800798, -2.5768776, 4.141674, -1.5362737, -2.668735, 4.1336055, -1.777915, -2.931157, 4.2222066, -1.5160246, -2.668735, 4.1336055, -1.777915, -2.5768776, 4.141674, -1.5362737, -2.4587846, 3.9438598, -1.5800798, -2.668735, 4.1336055, -1.777915, -2.5768776, 4.0332108, -1.9111595, -3.4035153, 3.9438598, -1.5800798, -3.2854366, 4.141674, -1.5362737, -3.1935806, 3.9892077, -1.288969, -3.1935806, 3.9892077, -1.288969, -3.2854366, 4.141674, -1.5362737, -2.931157, 4.2222066, -1.5160246, -3.2854366, 4.0332108, -1.9111595, -3.2854366, 4.141674, -1.5362737, -3.4035153, 3.9438598, -1.5800798, -2.931157, 4.2177896, -1.8485272, -2.5768776, 4.0332108, -1.9111595, -2.668735, 4.1336055, -1.777915, -2.931157, 4.2222066, -1.5160246, -2.931157, 4.2177896, -1.8485272, -2.668735, 4.1336055, -1.777915, -3.2854366, 4.141674, -1.5362737, -2.931157, 4.2177896, -1.8485272, -2.931157, 4.2222066, -1.5160246, -2.931157, 4.2177896, -1.8485272, -2.931157, 4.0577416, -2.0247965, -2.5768776, 4.0332108, -1.9111595, -3.2854366, 4.0332108, -1.9111595, -3.1935806, 4.1336055, -1.777915, -3.2854366, 4.141674, -1.5362737, -3.2854366, 4.141674, -1.5362737, -3.1935806, 4.1336055, -1.777915, -2.931157, 4.2177896, -1.8485272, -3.1935806, 4.1336055, -1.777915, -3.2854366, 4.0332108, -1.9111595, -2.931157, 4.0577416, -2.0247965, -3.1935806, 4.1336055, -1.777915, -2.931157, 4.0577416, -2.0247965, -2.931157, 4.2177896, -1.8485272, -3.034594, 3.324797, -1.6474974, -2.9311528, 3.431215, -1.5348387, -2.9311528, 3.2713995, -1.6237905, -3.034594, 3.324797, -1.6474974, -3.0708017, 3.4427564, -1.5799403, -2.9311528, 3.431215, -1.5348387, -3.0708017, 3.3060207, -1.7590828, -3.034594, 3.324797, -1.6474974, -2.9311528, 3.2713995, -1.6237905, -3.0708017, 3.3060207, -1.7590828, -3.0708017, 3.4427564, -1.5799403, -3.034594, 3.324797, -1.6474974, -3.0708017, 3.4427564, -1.5799403, -2.9311528, 3.7502108, -1.490046, -2.9311528, 3.431215, -1.5348387, -2.9311528, 3.431215, -1.5348387, -2.9311528, 3.7502108, -1.490046, -2.8277116, 3.759186, -1.5251193, -3.0708017, 3.4427564, -1.5799403, -3.034594, 3.759186, -1.5251193, -2.9311528, 3.7502108, -1.490046, -2.9311528, 3.431215, -1.5348387, -2.8277116, 3.759186, -1.5251193, -2.791504, 3.4427564, -1.5799403, -2.9311528, 3.2713995, -1.6237905, -2.9311528, 3.431215, -1.5348387, -2.791504, 3.4427564, -1.5799403, -2.791504, 3.4427564, -1.5799403, -2.8277116, 3.759186, -1.5251193, -2.791504, 3.784829, -1.6253259, -3.1173468, 3.477378, -1.7152324, -3.034594, 3.759186, -1.5251193, -3.0708017, 3.4427564, -1.5799403, -3.0708017, 3.3060207, -1.7590828, -3.1173468, 3.477378, -1.7152324, -3.0708017, 3.4427564, -1.5799403, -3.1173468, 3.477378, -1.7152324, -3.0708017, 3.784829, -1.6253259, -3.034594, 3.759186, -1.5251193, -2.791504, 3.4427564, -1.5799403, -2.791504, 3.784829, -1.6253259, -2.7449532, 3.477378, -1.7152324, -2.7449532, 3.477378, -1.7152324, -2.791504, 3.784829, -1.6253259, -2.8277116, 3.8104749, -1.7255449, -2.9311528, 3.2713995, -1.6237905, -2.791504, 3.4427564, -1.5799403, -2.8277116, 3.324797, -1.6474974, -2.8277116, 3.324797, -1.6474974, -2.791504, 3.4427564, -1.5799403, -2.7449532, 3.477378, -1.7152324, -3.0708017, 3.3060207, -1.7590828, -2.9311528, 3.2713995, -1.6237905, -2.9311528, 3.2488918, -1.7737021, -2.9311528, 3.2488918, -1.7737021, -2.9311528, 3.2713995, -1.6237905, -2.8277116, 3.324797, -1.6474974, -3.034594, 3.3760855, -1.8479218, -3.1173468, 3.477378, -1.7152324, -3.0708017, 3.3060207, -1.7590828, -3.034594, 3.3760855, -1.8479218, -3.0708017, 3.3060207, -1.7590828, -2.9311528, 3.2488918, -1.7737021, -3.0708017, 3.511996, -1.8505123, -3.0708017, 3.784829, -1.6253259, -3.1173468, 3.477378, -1.7152324, -3.034594, 3.3760855, -1.8479218, -3.0708017, 3.511996, -1.8505123, -3.1173468, 3.477378, -1.7152324, -3.0708017, 3.511996, -1.8505123, -3.034594, 3.8104749, -1.7255449, -3.0708017, 3.784829, -1.6253259, -3.034594, 3.3760855, -1.8479218, -2.9311528, 3.2488918, -1.7737021, -2.9311528, 3.3406389, -1.8943624, -3.0708017, 3.511996, -1.8505123, -3.034594, 3.3760855, -1.8479218, -2.9311528, 3.3406389, -1.8943624, -2.9311528, 3.2488918, -1.7737021, -2.8277116, 3.324797, -1.6474974, -2.791504, 3.3060207, -1.7590828, -2.9311528, 3.3406389, -1.8943624, -2.9311528, 3.2488918, -1.7737021, -2.791504, 3.3060207, -1.7590828, -2.8277116, 3.324797, -1.6474974, -2.7449532, 3.477378, -1.7152324, -2.791504, 3.3060207, -1.7590828, -2.9311528, 3.3406389, -1.8943624, -2.791504, 3.3060207, -1.7590828, -2.8277116, 3.3760855, -1.8479218, -3.034594, 3.8104749, -1.7255449, -3.0708017, 3.511996, -1.8505123, -2.9311528, 3.5235374, -1.8956139, -3.0708017, 3.511996, -1.8505123, -2.9311528, 3.3406389, -1.8943624, -2.9311528, 3.5235374, -1.8956139, -2.9311528, 3.5235374, -1.8956139, -2.9311528, 3.3406389, -1.8943624, -2.8277116, 3.3760855, -1.8479218, -3.034594, 3.8104749, -1.7255449, -2.9311528, 3.5235374, -1.8956139, -2.9311528, 3.8194501, -1.7606182, -2.791504, 3.3060207, -1.7590828, -2.7449532, 3.477378, -1.7152324, -2.791504, 3.511996, -1.8505123, -2.791504, 3.3060207, -1.7590828, -2.791504, 3.511996, -1.8505123, -2.8277116, 3.3760855, -1.8479218, -2.9311528, 3.5235374, -1.8956139, -2.8277116, 3.3760855, -1.8479218, -2.791504, 3.511996, -1.8505123, -2.9311528, 3.8194501, -1.7606182, -2.9311528, 3.5235374, -1.8956139, -2.791504, 3.511996, -1.8505123, -2.7449532, 3.477378, -1.7152324, -2.8277116, 3.8104749, -1.7255449, -2.791504, 3.511996, -1.8505123, -2.9311528, 3.8194501, -1.7606182, -2.791504, 3.511996, -1.8505123, -2.8277116, 3.8104749, -1.7255449, -2.9921641, 3.357846, -2.219953, -2.9311528, 3.439559, -2.179008, -2.9311528, 3.3330348, -2.1960335, -2.9921641, 3.357846, -2.219953, -3.0135126, 3.436851, -2.2063189, -2.9311528, 3.439559, -2.179008, -3.0135126, 3.324916, -2.2780023, -2.9921641, 3.357846, -2.219953, -2.9311528, 3.3330348, -2.1960335, -3.0135126, 3.324916, -2.2780023, -3.0135126, 3.436851, -2.2063189, -2.9921641, 3.357846, -2.219953, -3.0135126, 3.436851, -2.2063189, -2.9311528, 3.6253839, -2.2185574, -2.9311528, 3.439559, -2.179008, -2.9311528, 3.439559, -2.179008, -2.9311528, 3.6253839, -2.2185574, -2.8701415, 3.6232789, -2.239798, -3.0135126, 3.436851, -2.2063189, -2.9921641, 3.6232789, -2.239798, -2.9311528, 3.6253839, -2.2185574, -2.9311528, 3.439559, -2.179008, -2.8701415, 3.6232789, -2.239798, -2.848793, 3.436851, -2.2063189, -2.9311528, 3.3330348, -2.1960335, -2.9311528, 3.439559, -2.179008, -2.848793, 3.436851, -2.2063189, -2.848793, 3.436851, -2.2063189, -2.8701415, 3.6232789, -2.239798, -2.848793, 3.617262, -2.3005147, -3.0409703, 3.428742, -2.2882862, -2.9921641, 3.6232789, -2.239798, -3.0135126, 3.436851, -2.2063189, -3.0135126, 3.324916, -2.2780023, -3.0409703, 3.428742, -2.2882862, -3.0135126, 3.436851, -2.2063189, -3.0409703, 3.428742, -2.2882862, -3.0135126, 3.617262, -2.3005147, -2.9921641, 3.6232789, -2.239798, -2.848793, 3.436851, -2.2063189, -2.848793, 3.617262, -2.3005147, -2.8213353, 3.428742, -2.2882862, -2.8213353, 3.428742, -2.2882862, -2.848793, 3.617262, -2.3005147, -2.8701415, 3.6112561, -2.361228, -2.9311528, 3.3330348, -2.1960335, -2.848793, 3.436851, -2.2063189, -2.8701415, 3.357846, -2.219953, -2.8701415, 3.357846, -2.219953, -2.848793, 3.436851, -2.2063189, -2.8213353, 3.428742, -2.2882862, -3.0135126, 3.324916, -2.2780023, -2.9311528, 3.3330348, -2.1960335, -2.9311528, 3.2903087, -2.274579, -2.9311528, 3.2903087, -2.274579, -2.9311528, 3.3330348, -2.1960335, -2.8701415, 3.357846, -2.219953, -2.9921641, 3.345823, -2.3413825, -3.0409703, 3.428742, -2.2882862, -3.0135126, 3.324916, -2.2780023, -2.9921641, 3.345823, -2.3413825, -3.0135126, 3.324916, -2.2780023, -2.9311528, 3.2903087, -2.274579, -3.0135126, 3.4206212, -2.370242, -3.0135126, 3.617262, -2.3005147, -3.0409703, 3.428742, -2.2882862, -2.9921641, 3.345823, -2.3413825, -3.0135126, 3.4206212, -2.370242, -3.0409703, 3.428742, -2.2882862, -3.0135126, 3.4206212, -2.370242, -2.9921641, 3.6112561, -2.361228, -3.0135126, 3.617262, -2.3005147, -2.9921641, 3.345823, -2.3413825, -2.9311528, 3.2903087, -2.274579, -2.9311528, 3.3167953, -2.3599591, -3.0135126, 3.4206212, -2.370242, -2.9921641, 3.345823, -2.3413825, -2.9311528, 3.3167953, -2.3599591, -2.9311528, 3.2903087, -2.274579, -2.8701415, 3.357846, -2.219953, -2.848793, 3.324916, -2.2780023, -2.9311528, 3.3167953, -2.3599591, -2.9311528, 3.2903087, -2.274579, -2.848793, 3.324916, -2.2780023, -2.8701415, 3.357846, -2.219953, -2.8213353, 3.428742, -2.2882862, -2.848793, 3.324916, -2.2780023, -2.9311528, 3.3167953, -2.3599591, -2.848793, 3.324916, -2.2780023, -2.8701415, 3.345823, -2.3413825, -2.9921641, 3.6112561, -2.361228, -3.0135126, 3.4206212, -2.370242, -2.9311528, 3.417915, -2.3975658, -3.0135126, 3.4206212, -2.370242, -2.9311528, 3.3167953, -2.3599591, -2.9311528, 3.417915, -2.3975658, -2.9311528, 3.417915, -2.3975658, -2.9311528, 3.3167953, -2.3599591, -2.8701415, 3.345823, -2.3413825, -2.9921641, 3.6112561, -2.361228, -2.9311528, 3.417915, -2.3975658, -2.9311528, 3.609141, -2.3824706, -2.848793, 3.324916, -2.2780023, -2.8213353, 3.428742, -2.2882862, -2.848793, 3.4206212, -2.370242, -2.848793, 3.324916, -2.2780023, -2.848793, 3.4206212, -2.370242, -2.8701415, 3.345823, -2.3413825, -2.9311528, 3.417915, -2.3975658, -2.8701415, 3.345823, -2.3413825, -2.848793, 3.4206212, -2.370242, -2.9311528, 3.609141, -2.3824706, -2.9311528, 3.417915, -2.3975658, -2.848793, 3.4206212, -2.370242, -2.8213353, 3.428742, -2.2882862, -2.8701415, 3.6112561, -2.361228, -2.848793, 3.4206212, -2.370242, -2.9311528, 3.609141, -2.3824706, -2.848793, 3.4206212, -2.370242, -2.8701415, 3.6112561, -2.361228, -3.085928, 3.6077986, -2.0964143, -2.9311543, 3.695728, -1.988307, -2.9311543, 3.5811217, -2.0463157, -2.9311543, 3.5811217, -2.0463157, -2.9311543, 3.695728, -1.988307, -2.7222042, 3.6880543, -2.0503378, -3.085928, 3.6077986, -2.0964143, -3.1401043, 3.6880543, -2.0503378, -2.9311543, 3.695728, -1.988307, -2.9311543, 3.5811217, -2.0463157, -2.7222042, 3.6880543, -2.0503378, -2.7763805, 3.6077986, -2.0964143, -3.1401043, 3.6073084, -2.2966123, -3.1401043, 3.6880543, -2.0503378, -3.085928, 3.6077986, -2.0964143, -2.7763805, 3.6077986, -2.0964143, -2.7222042, 3.6880543, -2.0503378, -2.6525536, 3.7145243, -2.3075337, -3.1401043, 3.6073084, -2.2966123, -3.2097464, 3.7145243, -2.3075337, -3.1401043, 3.6880543, -2.0503378, -2.7763805, 3.6077986, -2.0964143, -2.6525536, 3.7145243, -2.3075337, -2.7222042, 3.6073084, -2.2966123, -3.085928, 3.6335886, -2.4534798, -3.2097464, 3.7145243, -2.3075337, -3.1401043, 3.6073084, -2.2966123, -2.7222042, 3.6073084, -2.2966123, -2.6525536, 3.7145243, -2.3075337, -2.7222042, 3.697219, -2.5090442, -3.085928, 3.6335886, -2.4534798, -3.1401043, 3.697219, -2.5090442, -3.2097464, 3.7145243, -2.3075337, -2.7222042, 3.6073084, -2.2966123, -2.7222042, 3.697219, -2.5090442, -2.7763805, 3.6335886, -2.4534798, -3.1401043, 3.697219, -2.5090442, -3.085928, 3.6335886, -2.4534798, -2.9311543, 3.5909529, -2.5021935, -2.9311543, 3.687878, -2.5769706, -2.7763805, 3.6335886, -2.4534798, -2.7222042, 3.697219, -2.5090442, -3.1401043, 3.697219, -2.5090442, -2.9311543, 3.5909529, -2.5021935, -2.9311543, 3.687878, -2.5769706, -2.9311543, 3.687878, -2.5769706, -2.9311543, 3.5909529, -2.5021935, -2.7763805, 3.6335886, -2.4534798, -2.9311543, 3.695728, -1.988307, -2.7763805, 3.7984102, -2.1553535, -2.7222042, 3.6880543, -2.0503378, -2.7222042, 3.6880543, -2.0503378, -2.7763805, 3.7984102, -2.1553535, -2.7222042, 3.8329906, -2.3231764, -2.9311543, 3.695728, -1.988307, -2.9311543, 3.845578, -2.1115575, -2.7763805, 3.7984102, -2.1553535, -2.9311543, 3.845578, -2.1115575, -2.7222042, 3.8329906, -2.3231764, -2.7763805, 3.7984102, -2.1553535, -2.7222042, 3.6880543, -2.0503378, -2.7222042, 3.8329906, -2.3231764, -2.6525536, 3.7145243, -2.3075337, -3.1401043, 3.6880543, -2.0503378, -2.9311543, 3.845578, -2.1115575, -2.9311543, 3.695728, -1.988307, -3.1401043, 3.6880543, -2.0503378, -3.085928, 3.7984102, -2.1553535, -2.9311543, 3.845578, -2.1115575, -3.2097464, 3.7145243, -2.3075337, -3.085928, 3.7984102, -2.1553535, -3.1401043, 3.6880543, -2.0503378, -2.9311543, 3.845578, -2.1115575, -2.9311543, 3.8817086, -2.3282094, -2.7222042, 3.8329906, -2.3231764, -3.085928, 3.7984102, -2.1553535, -2.9311543, 3.8817086, -2.3282094, -2.9311543, 3.845578, -2.1115575, -2.6525536, 3.7145243, -2.3075337, -2.7222042, 3.8329906, -2.3231764, -2.7763805, 3.7797475, -2.4554658, -2.9311543, 3.8817086, -2.3282094, -2.7763805, 3.7797475, -2.4554658, -2.7222042, 3.8329906, -2.3231764, -2.6525536, 3.7145243, -2.3075337, -2.7763805, 3.7797475, -2.4554658, -2.7222042, 3.697219, -2.5090442, -3.2097464, 3.7145243, -2.3075337, -3.1401043, 3.8329906, -2.3231764, -3.085928, 3.7984102, -2.1553535, -3.085928, 3.7984102, -2.1553535, -3.1401043, 3.8329906, -2.3231764, -2.9311543, 3.8817086, -2.3282094, -3.1401043, 3.697219, -2.5090442, -3.1401043, 3.8329906, -2.3231764, -3.2097464, 3.7145243, -2.3075337, -2.9311543, 3.812152, -2.5115814, -2.7222042, 3.697219, -2.5090442, -2.7763805, 3.7797475, -2.4554658, -2.9311543, 3.8817086, -2.3282094, -2.9311543, 3.812152, -2.5115814, -2.7763805, 3.7797475, -2.4554658, -3.1401043, 3.8329906, -2.3231764, -2.9311543, 3.812152, -2.5115814, -2.9311543, 3.8817086, -2.3282094, -2.9311543, 3.812152, -2.5115814, -2.9311543, 3.687878, -2.5769706, -2.7222042, 3.697219, -2.5090442, -3.1401043, 3.697219, -2.5090442, -3.085928, 3.7797475, -2.4554658, -3.1401043, 3.8329906, -2.3231764, -3.1401043, 3.8329906, -2.3231764, -3.085928, 3.7797475, -2.4554658, -2.9311543, 3.812152, -2.5115814, -3.085928, 3.7797475, -2.4554658, -3.1401043, 3.697219, -2.5090442, -2.9311543, 3.687878, -2.5769706, -3.085928, 3.7797475, -2.4554658, -2.9311543, 3.687878, -2.5769706, -2.9311543, 3.812152, -2.5115814, -16.306217, 3.8995876, -8.693363, -16.639778, 3.6870875, -8.751106, -16.484772, 3.8599336, -8.475517, -16.306217, 3.8995876, -8.693363, -16.341208, 3.7667115, -8.942926, -16.639778, 3.6870875, -8.751106, -16.484772, 3.8599336, -8.475517, -16.639778, 3.6870875, -8.751106, -16.958773, 3.7667115, -8.595573, -16.484772, 3.8599336, -8.475517, -16.958773, 3.7667115, -8.595573, -16.763668, 3.8995876, -8.436068, -16.639778, 3.6870875, -8.751106, -17.035208, 3.6513293, -8.918842, -16.958773, 3.7667115, -8.595573, -16.639778, 3.6870875, -8.751106, -16.83802, 3.579765, -9.103563, -17.035208, 3.6513293, -8.918842, -16.341208, 3.7667115, -8.942926, -16.83802, 3.579765, -9.103563, -16.639778, 3.6870875, -8.751106, -16.341208, 3.7667115, -8.942926, -16.577757, 3.6513293, -9.17614, -16.83802, 3.579765, -9.103563, -16.108875, 3.6335871, -8.122934, -16.322905, 3.5589838, -8.187733, -16.219904, 3.5909493, -8.004604, -16.108875, 3.6335871, -8.122934, -16.138561, 3.607314, -8.286205, -16.322905, 3.5589838, -8.187733, -16.219904, 3.5909493, -8.004604, -16.322905, 3.5589838, -8.187733, -16.502796, 3.607314, -8.081339, -16.219904, 3.5909493, -8.004604, -16.502796, 3.607314, -8.081339, -16.378687, 3.6335871, -7.9711776, -16.322905, 3.5589838, -8.187733, -16.553732, 3.6077974, -8.2823925, -16.502796, 3.607314, -8.081339, -16.322905, 3.5589838, -8.187733, -16.443384, 3.5811167, -8.401934, -16.553732, 3.6077974, -8.2823925, -16.138561, 3.607314, -8.286205, -16.443384, 3.5811167, -8.401934, -16.322905, 3.5589838, -8.187733, -16.138561, 3.607314, -8.286205, -16.28392, 3.6077974, -8.434149, -16.443384, 3.5811167, -8.401934, -16.577753, 3.651332, -9.176138, -16.915924, 3.7287025, -9.242073, -16.838017, 3.5797634, -9.1035595, -16.838017, 3.5797634, -9.1035595, -16.915924, 3.7287025, -9.242073, -17.174082, 3.7524595, -8.97838, -16.577753, 3.651332, -9.176138, -16.556507, 3.7524595, -9.325739, -16.915924, 3.7287025, -9.242073, -16.838017, 3.5797634, -9.1035595, -17.174082, 3.7524595, -8.97838, -17.035204, 3.651332, -8.91884, -16.341192, 3.766715, -8.942926, -16.556507, 3.7524595, -9.325739, -16.577753, 3.651332, -9.176138, -17.035204, 3.651332, -8.91884, -17.174082, 3.7524595, -8.97838, -17.083666, 3.943858, -8.576735, -16.341192, 3.766715, -8.942926, -16.260246, 3.943858, -9.039871, -16.556507, 3.7524595, -9.325739, -17.035204, 3.651332, -8.91884, -17.083666, 3.943858, -8.576735, -16.958767, 3.7667148, -8.595568, -16.306215, 3.8995948, -8.693367, -16.260246, 3.943858, -9.039871, -16.341192, 3.766715, -8.942926, -16.958767, 3.7667148, -8.595568, -17.083666, 3.943858, -8.576735, -16.81843, 4.033209, -8.346062, -16.306215, 3.8995948, -8.693367, -16.200855, 4.033209, -8.6934185, -16.260246, 3.943858, -9.039871, -16.958767, 3.7667148, -8.595568, -16.81843, 4.033209, -8.346062, -16.763666, 3.8995945, -8.4360695, -16.200855, 4.033209, -8.6934185, -16.306215, 3.8995948, -8.693367, -16.484772, 3.8599298, -8.475521, -16.453936, 4.0577397, -8.420694, -16.763666, 3.8995945, -8.4360695, -16.81843, 4.033209, -8.346062, -16.200855, 4.033209, -8.6934185, -16.484772, 3.8599298, -8.475521, -16.453936, 4.0577397, -8.420694, -16.453936, 4.0577397, -8.420694, -16.484772, 3.8599298, -8.475521, -16.763666, 3.8995945, -8.4360695, -16.915924, 3.7287025, -9.242073, -17.043386, 3.9892056, -8.933388, -17.174082, 3.7524595, -8.97838, -17.174082, 3.7524595, -8.97838, -17.043386, 3.9892056, -8.933388, -17.00221, 4.141672, -8.672808, -16.915924, 3.7287025, -9.242073, -16.862286, 4.038946, -9.146708, -17.043386, 3.9892056, -8.933388, -16.862286, 4.038946, -9.146708, -17.00221, 4.141672, -8.672808, -17.043386, 3.9892056, -8.933388, -17.174082, 3.7524595, -8.97838, -17.00221, 4.141672, -8.672808, -17.083666, 3.943858, -8.576735, -16.556507, 3.7524595, -9.325739, -16.862286, 4.038946, -9.146708, -16.915924, 3.7287025, -9.242073, -16.556507, 3.7524595, -9.325739, -16.585936, 3.9892056, -9.190685, -16.862286, 4.038946, -9.146708, -16.260246, 3.943858, -9.039871, -16.585936, 3.9892056, -9.190685, -16.556507, 3.7524595, -9.325739, -16.862286, 4.038946, -9.146708, -16.703352, 4.2222047, -8.864136, -17.00221, 4.141672, -8.672808, -16.585936, 3.9892056, -9.190685, -16.703352, 4.2222047, -8.864136, -16.862286, 4.038946, -9.146708, -17.083666, 3.943858, -8.576735, -17.00221, 4.141672, -8.672808, -16.803688, 4.1336036, -8.507227, -16.703352, 4.2222047, -8.864136, -16.803688, 4.1336036, -8.507227, -17.00221, 4.141672, -8.672808, -17.083666, 3.943858, -8.576735, -16.803688, 4.1336036, -8.507227, -16.81843, 4.033209, -8.346062, -16.260246, 3.943858, -9.039871, -16.384638, 4.141672, -9.020166, -16.585936, 3.9892056, -9.190685, -16.585936, 3.9892056, -9.190685, -16.384638, 4.141672, -9.020166, -16.703352, 4.2222047, -8.864136, -16.200855, 4.033209, -8.6934185, -16.384638, 4.141672, -9.020166, -16.260246, 3.943858, -9.039871, -16.540348, 4.2177877, -8.574329, -16.81843, 4.033209, -8.346062, -16.803688, 4.1336036, -8.507227, -16.703352, 4.2222047, -8.864136, -16.540348, 4.2177877, -8.574329, -16.803688, 4.1336036, -8.507227, -16.384638, 4.141672, -9.020166, -16.540348, 4.2177877, -8.574329, -16.703352, 4.2222047, -8.864136, -16.540348, 4.2177877, -8.574329, -16.453936, 4.0577397, -8.420694, -16.81843, 4.033209, -8.346062, -16.200855, 4.033209, -8.6934185, -16.346237, 4.1336036, -8.764523, -16.384638, 4.141672, -9.020166, -16.384638, 4.141672, -9.020166, -16.346237, 4.1336036, -8.764523, -16.540348, 4.2177877, -8.574329, -16.346237, 4.1336036, -8.764523, -16.200855, 4.033209, -8.6934185, -16.453936, 4.0577397, -8.420694, -16.346237, 4.1336036, -8.764523, -16.453936, 4.0577397, -8.420694, -16.540348, 4.2177877, -8.574329, -16.548744, 3.324795, -8.800255, -16.694132, 3.4312131, -8.847736, -16.650524, 3.2713976, -8.770206, -16.548744, 3.324795, -8.800255, -16.550304, 3.4427547, -8.876886, -16.694132, 3.4312131, -8.847736, -16.462482, 3.3060188, -8.720747, -16.548744, 3.324795, -8.800255, -16.650524, 3.2713976, -8.770206, -16.462482, 3.3060188, -8.720747, -16.550304, 3.4427547, -8.876886, -16.548744, 3.324795, -8.800255, -16.550304, 3.4427547, -8.876886, -16.716091, 3.7502089, -8.886778, -16.694132, 3.4312131, -8.847736, -16.694132, 3.4312131, -8.847736, -16.716091, 3.7502089, -8.886778, -16.789055, 3.7591841, -8.805498, -16.550304, 3.4427547, -8.876886, -16.608738, 3.7591841, -8.906918, -16.716091, 3.7502089, -8.886778, -16.694132, 3.4312131, -8.847736, -16.789055, 3.7591841, -8.805498, -16.793737, 3.4427545, -8.739965, -16.650524, 3.2713976, -8.770206, -16.694132, 3.4312131, -8.847736, -16.793737, 3.4427545, -8.739965, -16.793737, 3.4427545, -8.739965, -16.789055, 3.7591841, -8.805498, -16.771488, 3.784827, -8.700407, -16.44341, 3.477376, -8.781784, -16.608738, 3.7591841, -8.906918, -16.550304, 3.4427547, -8.876886, -16.462482, 3.3060188, -8.720747, -16.44341, 3.477376, -8.781784, -16.550304, 3.4427547, -8.876886, -16.44341, 3.477376, -8.781784, -16.528055, 3.784827, -8.837328, -16.608738, 3.7591841, -8.906918, -16.793737, 3.4427545, -8.739965, -16.771488, 3.784827, -8.700407, -16.767986, 3.477376, -8.599226, -16.767986, 3.477376, -8.599226, -16.771488, 3.784827, -8.700407, -16.6908, 3.810473, -8.630808, -16.650524, 3.2713976, -8.770206, -16.793737, 3.4427545, -8.739965, -16.729061, 3.324795, -8.698833, -16.729061, 3.324795, -8.698833, -16.793737, 3.4427545, -8.739965, -16.767986, 3.477376, -8.599226, -16.462482, 3.3060188, -8.720747, -16.650524, 3.2713976, -8.770206, -16.577034, 3.2488902, -8.639545, -16.577034, 3.2488902, -8.639545, -16.650524, 3.2713976, -8.770206, -16.729061, 3.324795, -8.698833, -16.450489, 3.3760836, -8.6255665, -16.44341, 3.477376, -8.781784, -16.462482, 3.3060188, -8.720747, -16.450489, 3.3760836, -8.6255665, -16.462482, 3.3060188, -8.720747, -16.577034, 3.2488902, -8.639545, -16.417662, 3.5119941, -8.641058, -16.528055, 3.784827, -8.837328, -16.44341, 3.477376, -8.781784, -16.450489, 3.3760836, -8.6255665, -16.417662, 3.5119941, -8.641058, -16.44341, 3.477376, -8.781784, -16.417662, 3.5119941, -8.641058, -16.510483, 3.810473, -8.732229, -16.528055, 3.784827, -8.837328, -16.450489, 3.3760836, -8.6255665, -16.577034, 3.2488902, -8.639545, -16.517881, 3.340637, -8.534378, -16.417662, 3.5119941, -8.641058, -16.450489, 3.3760836, -8.6255665, -16.517881, 3.340637, -8.534378, -16.577034, 3.2488902, -8.639545, -16.729061, 3.324795, -8.698833, -16.705917, 3.3060188, -8.583826, -16.517881, 3.340637, -8.534378, -16.577034, 3.2488902, -8.639545, -16.705917, 3.3060188, -8.583826, -16.729061, 3.324795, -8.698833, -16.767986, 3.477376, -8.599226, -16.705917, 3.3060188, -8.583826, -16.517881, 3.340637, -8.534378, -16.705917, 3.3060188, -8.583826, -16.630806, 3.3760836, -8.524145, -16.510483, 3.810473, -8.732229, -16.417662, 3.5119941, -8.641058, -16.517267, 3.5235355, -8.533287, -16.417662, 3.5119941, -8.641058, -16.517881, 3.340637, -8.534378, -16.517267, 3.5235355, -8.533287, -16.517267, 3.5235355, -8.533287, -16.517881, 3.340637, -8.534378, -16.630806, 3.3760836, -8.524145, -16.510483, 3.810473, -8.732229, -16.517267, 3.5235355, -8.533287, -16.583447, 3.8194482, -8.6509495, -16.705917, 3.3060188, -8.583826, -16.767986, 3.477376, -8.599226, -16.661095, 3.5119941, -8.504137, -16.705917, 3.3060188, -8.583826, -16.661095, 3.5119941, -8.504137, -16.630806, 3.3760836, -8.524145, -16.517267, 3.5235355, -8.533287, -16.630806, 3.3760836, -8.524145, -16.661095, 3.5119941, -8.504137, -16.583447, 3.8194482, -8.6509495, -16.517267, 3.5235355, -8.533287, -16.661095, 3.5119941, -8.504137, -16.767986, 3.477376, -8.599226, -16.6908, 3.810473, -8.630808, -16.661095, 3.5119941, -8.504137, -16.583447, 3.8194482, -8.6509495, -16.661095, 3.5119941, -8.504137, -16.6908, 3.810473, -8.630808, -16.30509, 3.357844, -8.280506, -16.37834, 3.439557, -8.2862835, -16.369993, 3.333033, -8.271444, -16.30509, 3.357844, -8.280506, -16.293167, 3.436849, -8.302855, -16.37834, 3.439557, -8.2862835, -16.258024, 3.324914, -8.240377, -16.30509, 3.357844, -8.280506, -16.369993, 3.333033, -8.271444, -16.258024, 3.324914, -8.240377, -16.293167, 3.436849, -8.302855, -16.30509, 3.357844, -8.280506, -16.293167, 3.436849, -8.302855, -16.358952, 3.625382, -8.251812, -16.37834, 3.439557, -8.2862835, -16.37834, 3.439557, -8.2862835, -16.358952, 3.625382, -8.251812, -16.401714, 3.6232772, -8.20339, -16.293167, 3.436849, -8.302855, -16.29536, 3.6232772, -8.263208, -16.358952, 3.625382, -8.251812, -16.37834, 3.439557, -8.2862835, -16.401714, 3.6232772, -8.20339, -16.436735, 3.436849, -8.222103, -16.369993, 3.333033, -8.271444, -16.37834, 3.439557, -8.2862835, -16.436735, 3.436849, -8.222103, -16.436735, 3.436849, -8.222103, -16.401714, 3.6232772, -8.20339, -16.390556, 3.61726, -8.140003, -16.229052, 3.4287403, -8.244873, -16.29536, 3.6232772, -8.263208, -16.293167, 3.436849, -8.302855, -16.258024, 3.324914, -8.240377, -16.229052, 3.4287403, -8.244873, -16.293167, 3.436849, -8.302855, -16.229052, 3.4287403, -8.244873, -16.246988, 3.61726, -8.220755, -16.29536, 3.6232772, -8.263208, -16.436735, 3.436849, -8.222103, -16.390556, 3.61726, -8.140003, -16.420483, 3.4287403, -8.137201, -16.420483, 3.4287403, -8.137201, -16.390556, 3.61726, -8.140003, -16.342186, 3.6112542, -8.097551, -16.369993, 3.333033, -8.271444, -16.436735, 3.436849, -8.222103, -16.411444, 3.357844, -8.220686, -16.411444, 3.357844, -8.220686, -16.436735, 3.436849, -8.222103, -16.420483, 3.4287403, -8.137201, -16.258024, 3.324914, -8.240377, -16.369993, 3.333033, -8.271444, -16.331488, 3.290307, -8.202984, -16.331488, 3.290307, -8.202984, -16.369993, 3.333033, -8.271444, -16.411444, 3.357844, -8.220686, -16.245562, 3.3458211, -8.174669, -16.229052, 3.4287403, -8.244873, -16.258024, 3.324914, -8.240377, -16.245562, 3.3458211, -8.174669, -16.258024, 3.324914, -8.240377, -16.331488, 3.290307, -8.202984, -16.212805, 3.4206195, -8.159981, -16.246988, 3.61726, -8.220755, -16.229052, 3.4287403, -8.244873, -16.245562, 3.3458211, -8.174669, -16.212805, 3.4206195, -8.159981, -16.229052, 3.4287403, -8.244873, -16.212805, 3.4206195, -8.159981, -16.235832, 3.6112542, -8.1573715, -16.246988, 3.61726, -8.220755, -16.245562, 3.3458211, -8.174669, -16.331488, 3.290307, -8.202984, -16.28963, 3.3167934, -8.128569, -16.212805, 3.4206195, -8.159981, -16.245562, 3.3458211, -8.174669, -16.28963, 3.3167934, -8.128569, -16.331488, 3.290307, -8.202984, -16.411444, 3.357844, -8.220686, -16.401594, 3.324914, -8.159626, -16.28963, 3.3167934, -8.128569, -16.331488, 3.290307, -8.202984, -16.401594, 3.324914, -8.159626, -16.411444, 3.357844, -8.220686, -16.420483, 3.4287403, -8.137201, -16.401594, 3.324914, -8.159626, -16.28963, 3.3167934, -8.128569, -16.401594, 3.324914, -8.159626, -16.351915, 3.3458211, -8.114849, -16.235832, 3.6112542, -8.1573715, -16.212805, 3.4206195, -8.159981, -16.271194, 3.4179132, -8.095791, -16.212805, 3.4206195, -8.159981, -16.28963, 3.3167934, -8.128569, -16.271194, 3.4179132, -8.095791, -16.271194, 3.4179132, -8.095791, -16.28963, 3.3167934, -8.128569, -16.351915, 3.3458211, -8.114849, -16.235832, 3.6112542, -8.1573715, -16.271194, 3.4179132, -8.095791, -16.278595, 3.6091392, -8.108948, -16.401594, 3.324914, -8.159626, -16.420483, 3.4287403, -8.137201, -16.356375, 3.4206195, -8.079229, -16.401594, 3.324914, -8.159626, -16.356375, 3.4206195, -8.079229, -16.351915, 3.3458211, -8.114849, -16.271194, 3.4179132, -8.095791, -16.351915, 3.3458211, -8.114849, -16.356375, 3.4206195, -8.079229, -16.278595, 3.6091392, -8.108948, -16.271194, 3.4179132, -8.095791, -16.356375, 3.4206195, -8.079229, -16.420483, 3.4287403, -8.137201, -16.342186, 3.6112542, -8.097551, -16.356375, 3.4206195, -8.079229, -16.278595, 3.6091392, -8.108948, -16.356375, 3.4206195, -8.079229, -16.342186, 3.6112542, -8.097551, -16.283928, 3.6077967, -8.434147, -16.471827, 3.6957262, -8.4524975, -16.443388, 3.5811198, -8.4019375, -16.443388, 3.5811198, -8.4019375, -16.471827, 3.6957262, -8.4524975, -16.623535, 3.6880524, -8.295998, -16.283928, 3.6077967, -8.434147, -16.259296, 3.6880524, -8.500866, -16.471827, 3.6957262, -8.4524975, -16.443388, 3.5811198, -8.4019375, -16.623535, 3.6880524, -8.295998, -16.553728, 3.6077967, -8.282396, -16.138565, 3.6073067, -8.286215, -16.259296, 3.6880524, -8.500866, -16.283928, 3.6077967, -8.434147, -16.553728, 3.6077967, -8.282396, -16.623535, 3.6880524, -8.295998, -16.558157, 3.7145224, -8.0376835, -16.138565, 3.6073067, -8.286215, -16.072512, 3.7145224, -8.310837, -16.259296, 3.6880524, -8.500866, -16.553728, 3.6077967, -8.282396, -16.558157, 3.7145224, -8.0376835, -16.502804, 3.6073067, -8.0813465, -16.108883, 3.6335866, -8.122932, -16.072512, 3.7145224, -8.310837, -16.138565, 3.6073067, -8.286215, -16.502804, 3.6073067, -8.0813465, -16.558157, 3.7145224, -8.0376835, -16.398663, 3.697217, -7.896193, -16.108883, 3.6335866, -8.122932, -16.034424, 3.697217, -8.101061, -16.072512, 3.7145224, -8.310837, -16.502804, 3.6073067, -8.0813465, -16.398663, 3.697217, -7.896193, -16.378683, 3.6335866, -7.9711814, -16.034424, 3.697217, -8.101061, -16.108883, 3.6335866, -8.122932, -16.219902, 3.5909512, -8.004599, -16.183245, 3.687876, -7.939423, -16.378683, 3.6335866, -7.9711814, -16.398663, 3.697217, -7.896193, -16.034424, 3.697217, -8.101061, -16.219902, 3.5909512, -8.004599, -16.183245, 3.687876, -7.939423, -16.183245, 3.687876, -7.939423, -16.219902, 3.5909512, -8.004599, -16.378683, 3.6335866, -7.9711814, -16.471827, 3.6957262, -8.4524975, -16.524834, 3.7984083, -8.231026, -16.623535, 3.6880524, -8.295998, -16.623535, 3.6880524, -8.295998, -16.524834, 3.7984083, -8.231026, -16.48978, 3.8329887, -8.058193, -16.471827, 3.6957262, -8.4524975, -16.411404, 3.845576, -8.345074, -16.524834, 3.7984083, -8.231026, -16.411404, 3.845576, -8.345074, -16.48978, 3.8329887, -8.058193, -16.524834, 3.7984083, -8.231026, -16.623535, 3.6880524, -8.295998, -16.48978, 3.8329887, -8.058193, -16.558157, 3.7145224, -8.0376835, -16.259296, 3.6880524, -8.500866, -16.411404, 3.845576, -8.345074, -16.471827, 3.6957262, -8.4524975, -16.259296, 3.6880524, -8.500866, -16.255035, 3.7984083, -8.382776, -16.411404, 3.845576, -8.345074, -16.072512, 3.7145224, -8.310837, -16.255035, 3.7984083, -8.382776, -16.259296, 3.6880524, -8.500866, -16.411404, 3.845576, -8.345074, -16.305195, 3.881707, -8.156242, -16.48978, 3.8329887, -8.058193, -16.255035, 3.7984083, -8.382776, -16.305195, 3.881707, -8.156242, -16.411404, 3.845576, -8.345074, -16.558157, 3.7145224, -8.0376835, -16.48978, 3.8329887, -8.058193, -16.377708, 3.7797456, -7.9694505, -16.305195, 3.881707, -8.156242, -16.377708, 3.7797456, -7.9694505, -16.48978, 3.8329887, -8.058193, -16.558157, 3.7145224, -8.0376835, -16.377708, 3.7797456, -7.9694505, -16.398663, 3.697217, -7.896193, -16.072512, 3.7145224, -8.310837, -16.125544, 3.8329887, -8.263062, -16.255035, 3.7984083, -8.382776, -16.255035, 3.7984083, -8.382776, -16.125544, 3.8329887, -8.263062, -16.305195, 3.881707, -8.156242, -16.034424, 3.697217, -8.101061, -16.125544, 3.8329887, -8.263062, -16.072512, 3.7145224, -8.310837, -16.2153, 3.8121502, -7.9964156, -16.398663, 3.697217, -7.896193, -16.377708, 3.7797456, -7.9694505, -16.305195, 3.881707, -8.156242, -16.2153, 3.8121502, -7.9964156, -16.377708, 3.7797456, -7.9694505, -16.125544, 3.8329887, -8.263062, -16.2153, 3.8121502, -7.9964156, -16.305195, 3.881707, -8.156242, -16.2153, 3.8121502, -7.9964156, -16.183245, 3.687876, -7.939423, -16.398663, 3.697217, -7.896193, -16.034424, 3.697217, -8.101061, -16.10791, 3.7797456, -8.121201, -16.125544, 3.8329887, -8.263062, -16.125544, 3.8329887, -8.263062, -16.10791, 3.7797456, -8.121201, -16.2153, 3.8121502, -7.9964156, -16.10791, 3.7797456, -8.121201, -16.034424, 3.697217, -8.101061, -16.183245, 3.687876, -7.939423, -16.10791, 3.7797456, -8.121201, -16.183245, 3.687876, -7.939423, -16.2153, 3.8121502, -7.9964156, -7.086538, 9.0531025, 23.60468, -7.939819, 8.776951, 23.477636, -7.89693, 8.906714, 23.382841, -7.086538, 9.0531025, 23.60468, -7.1825204, 8.414909, 24.04463, -7.939819, 8.776951, 23.477636, -6.961786, 8.872082, 22.884235, -7.2900796, 8.958991, 23.246565, -7.2749104, 8.879946, 22.997782, -6.961786, 8.872082, 22.884235, -7.086538, 9.0531025, 23.60468, -7.2900796, 8.958991, 23.246565, -3.8860898, 6.9690914, 23.62356, -4.5446486, 7.7101564, 23.798996, -4.6237864, 7.655897, 23.206228, -3.8860898, 6.9690914, 23.62356, -3.8783517, 6.953966, 23.7307, -4.5446486, 7.7101564, 23.798996, -4.5446486, 7.7101564, 23.798996, -4.823362, 7.815732, 23.123234, -4.6237864, 7.655897, 23.206228, -4.5446486, 7.7101564, 23.798996, -4.899761, 7.976705, 23.460878, -4.823362, 7.815732, 23.123234, -4.899761, 7.976705, 23.460878, -4.5446486, 7.7101564, 23.798996, -5.2783494, 8.404766, 23.83276, -4.899761, 7.976705, 23.460878, -5.2783494, 8.404766, 23.83276, -5.3587403, 8.23149, 22.899555, -4.899761, 7.976705, 23.460878, -5.3587403, 8.23149, 22.899555, -5.0027404, 7.957439, 23.048418, -5.3587403, 8.23149, 22.899555, -5.5498886, 8.408549, 23.109316, -5.46656, 8.274823, 22.889973, -5.3587403, 8.23149, 22.899555, -5.2783494, 8.404766, 23.83276, -5.5498886, 8.408549, 23.109316, -6.134132, 8.667774, 22.83608, -6.545529, 8.861699, 23.146437, -6.347387, 8.715011, 22.84835, -6.134132, 8.667774, 22.83608, -6.2255015, 8.901371, 23.757235, -6.545529, 8.861699, 23.146437, -3.9216104, 6.895205, 23.810205, -4.5446486, 7.7101564, 23.798996, -3.8783517, 6.953966, 23.7307, -4.5446486, 7.7101564, 23.798996, -3.9216104, 6.895205, 23.810205, -4.32818, 7.252925, 23.874067, -4.32818, 7.252925, 23.874067, -3.9216104, 6.895205, 23.810205, -4.2883744, 7.0421944, 23.9518, -4.5446486, 7.7101564, 23.798996, -5.1261964, 7.754751, 24.078827, -5.2783494, 8.404766, 23.83276, -4.5446486, 7.7101564, 23.798996, -4.9056582, 7.542821, 24.083008, -5.1261964, 7.754751, 24.078827, -5.6396194, 7.681168, 24.366714, -6.2255015, 8.901371, 23.757235, -5.2783494, 8.404766, 23.83276, -6.2255015, 8.901371, 23.757235, -5.6396194, 7.681168, 24.366714, -6.046368, 7.8717294, 24.331638, -6.2255015, 8.901371, 23.757235, -6.046368, 7.8717294, 24.331638, -6.119331, 8.248653, 24.137571, -6.7146773, 8.708108, 23.8794, -7.086538, 9.0531025, 23.60468, -6.2255015, 8.901371, 23.757235, -7.086538, 9.0531025, 23.60468, -6.7146773, 8.708108, 23.8794, -7.0044417, 8.349307, 24.104824, -7.086538, 9.0531025, 23.60468, -7.0044417, 8.349307, 24.104824, -7.1825204, 8.414909, 24.04463, -6.2255015, 8.901371, 23.757235, -5.5498886, 8.408549, 23.109316, -5.2783494, 8.404766, 23.83276, -5.953386, 8.703066, 23.382, -5.5498886, 8.408549, 23.109316, -6.2255015, 8.901371, 23.757235, -5.5498886, 8.408549, 23.109316, -5.953386, 8.703066, 23.382, -5.8336997, 8.425533, 22.857294, -5.5498886, 8.408549, 23.109316, -5.8336997, 8.425533, 22.857294, -5.650786, 8.361013, 22.874142, -6.2255015, 8.901371, 23.757235, -5.9328055, 8.570662, 22.853338, -5.953386, 8.703066, 23.382, -6.2255015, 8.901371, 23.757235, -6.134132, 8.667774, 22.83608, -5.9328055, 8.570662, 22.853338, -6.119331, 8.248653, 24.137571, -6.4117084, 8.071957, 24.301369, -6.2255015, 8.901371, 23.757235, -6.119331, 8.248653, 24.137571, -6.237124, 7.9882894, 24.31635, -6.4117084, 8.071957, 24.301369, -7.086538, 9.0531025, 23.60468, -6.545529, 8.861699, 23.146437, -6.2255015, 8.901371, 23.757235, -6.545529, 8.861699, 23.146437, -7.086538, 9.0531025, 23.60468, -6.961786, 8.872082, 22.884235, -6.545529, 8.861699, 23.146437, -6.961786, 8.872082, 22.884235, -6.66091, 8.812481, 22.867199, -6.7146773, 8.708108, 23.8794, -6.4117084, 8.071957, 24.301369, -6.875569, 8.223388, 24.143358, -6.7146773, 8.708108, 23.8794, -6.2255015, 8.901371, 23.757235, -6.4117084, 8.071957, 24.301369, -7.89693, 8.906714, 23.382841, -7.2900796, 8.958991, 23.246565, -7.086538, 9.0531025, 23.60468, -7.2900796, 8.958991, 23.246565, -7.89693, 8.906714, 23.382841, -7.890431, 8.871303, 23.22045, -7.2900796, 8.958991, 23.246565, -7.890431, 8.871303, 23.22045, -7.484878, 8.8925495, 23.073898, -5.1261964, 7.754751, 24.078827, -5.6396194, 7.681168, 24.366714, -5.2783494, 8.404766, 23.83276, -5.1261964, 7.754751, 24.078827, -5.328693, 7.544381, 24.286783, -5.6396194, 7.681168, 24.366714, -4.9056582, 7.542821, 24.083008, -5.1280284, 7.4066963, 24.234045, -5.1261964, 7.754751, 24.078827, -4.9056582, 7.542821, 24.083008, -5.0212817, 7.379748, 24.207062, -5.1280284, 7.4066963, 24.234045, -4.9056582, 7.542821, 24.083008, -4.5446486, 7.7101564, 23.798996, -4.804087, 7.2903175, 24.15141, -4.32818, 7.252925, 23.874067, -4.804087, 7.2903175, 24.15141, -4.5446486, 7.7101564, 23.798996, -4.32818, 7.252925, 23.874067, -4.4737935, 7.144829, 24.02375, -4.804087, 7.2903175, 24.15141, -8.469263, 8.833129, 23.903402, -7.7564616, 8.8668, 23.1668, -7.7024093, 8.948188, 23.326448, -8.469263, 8.833129, 23.903402, -8.792166, 8.398045, 23.2395, -7.7564616, 8.8668, 23.1668, -7.939649, 8.319658, 24.36807, -8.019506, 8.693222, 23.9122, -7.814714, 8.502601, 24.04807, -7.939649, 8.319658, 24.36807, -8.469263, 8.833129, 23.903402, -8.019506, 8.693222, 23.9122, -10.742025, 5.3900876, 25.65433, -10.427696, 6.4519587, 25.310745, -9.86124, 6.2139215, 25.59392, -10.742025, 5.3900876, 25.65433, -10.839937, 5.4104342, 25.586737, -10.427696, 6.4519587, 25.310745, -10.427696, 6.4519587, 25.310745, -9.6490345, 6.4258494, 25.54795, -9.86124, 6.2139215, 25.59392, -10.427696, 6.4519587, 25.310745, -9.8865795, 6.741735, 25.338587, -9.6490345, 6.4258494, 25.54795, -9.8865795, 6.741735, 25.338587, -10.427696, 6.4519587, 25.310745, -9.939594, 7.468337, 24.97506, -9.8865795, 6.741735, 25.338587, -9.939594, 7.468337, 24.97506, -9.078133, 6.986556, 25.424217, -9.8865795, 6.741735, 25.338587, -9.078133, 6.986556, 25.424217, -9.458067, 6.6153927, 25.506601, -9.078133, 6.986556, 25.424217, -9.126747, 7.324047, 25.200722, -8.992649, 7.0699244, 25.362774, -9.078133, 6.986556, 25.424217, -9.939594, 7.468337, 24.97506, -9.126747, 7.324047, 25.200722, -8.48115, 7.732603, 24.99367, -8.460228, 8.226148, 24.537516, -8.340681, 7.875271, 24.831814, -8.48115, 7.732603, 24.99367, -9.20825, 8.35639, 24.448666, -8.460228, 8.226148, 24.537516, -10.878963, 5.39891, 25.472626, -10.427696, 6.4519587, 25.310745, -10.839937, 5.4104342, 25.586737, -10.427696, 6.4519587, 25.310745, -10.878963, 5.39891, 25.472626, -10.647385, 5.933868, 25.242573, -10.647385, 5.933868, 25.242573, -10.878963, 5.39891, 25.472626, -10.741941, 5.7328005, 25.138784, -10.427696, 6.4519587, 25.310745, -10.264824, 6.8368664, 24.649649, -9.939594, 7.468337, 24.97506, -10.427696, 6.4519587, 25.310745, -10.423819, 6.5334654, 24.740517, -10.264824, 6.8368664, 24.649649, -10.154926, 7.080727, 23.99825, -9.20825, 8.35639, 24.448666, -9.939594, 7.468337, 24.97506, -9.20825, 8.35639, 24.448666, -10.154926, 7.080727, 23.99825, -9.836353, 7.4272223, 23.768997, -9.20825, 8.35639, 24.448666, -9.836353, 7.4272223, 23.768997, -9.618614, 7.784581, 23.992184, -8.975733, 8.430757, 23.866367, -8.469263, 8.833129, 23.903402, -9.20825, 8.35639, 24.448666, -8.469263, 8.833129, 23.903402, -8.975733, 8.430757, 23.866367, -8.970964, 8.277404, 23.316208, -8.469263, 8.833129, 23.903402, -8.970964, 8.277404, 23.316208, -8.792166, 8.398045, 23.2395, -9.20825, 8.35639, 24.448666, -9.126747, 7.324047, 25.200722, -9.939594, 7.468337, 24.97506, -9.079264, 7.896097, 24.814846, -9.126747, 7.324047, 25.200722, -9.20825, 8.35639, 24.448666, -9.126747, 7.324047, 25.200722, -9.079264, 7.896097, 24.814846, -8.702198, 7.3584385, 25.153973, -9.126747, 7.324047, 25.200722, -8.702198, 7.3584385, 25.153973, -8.848383, 7.227356, 25.258986, -9.20825, 8.35639, 24.448666, -8.638075, 7.5534444, 25.106686, -9.079264, 7.896097, 24.814846, -9.20825, 8.35639, 24.448666, -8.48115, 7.732603, 24.99367, -8.638075, 7.5534444, 25.106686, -9.618614, 7.784581, 23.992184, -9.555359, 7.787944, 23.5661, -9.20825, 8.35639, 24.448666, -9.618614, 7.784581, 23.992184, -9.691614, 7.632971, 23.664253, -9.555359, 7.787944, 23.5661, -8.469263, 8.833129, 23.903402, -8.460228, 8.226148, 24.537516, -9.20825, 8.35639, 24.448666, -8.460228, 8.226148, 24.537516, -8.469263, 8.833129, 23.903402, -7.939649, 8.319658, 24.36807, -8.460228, 8.226148, 24.537516, -7.939649, 8.319658, 24.36807, -8.1381, 8.120274, 24.596785, -8.975733, 8.430757, 23.866367, -9.555359, 7.787944, 23.5661, -9.08618, 8.070576, 23.364449, -8.975733, 8.430757, 23.866367, -9.20825, 8.35639, 24.448666, -9.555359, 7.787944, 23.5661, -7.7024093, 8.948188, 23.326448, -8.019506, 8.693222, 23.9122, -8.469263, 8.833129, 23.903402, -8.019506, 8.693222, 23.9122, -7.7024093, 8.948188, 23.326448, -7.56801, 8.854256, 23.41686, -8.019506, 8.693222, 23.9122, -7.56801, 8.854256, 23.41686, -7.7330556, 8.652529, 23.835022, -10.264824, 6.8368664, 24.649649, -10.154926, 7.080727, 23.99825, -9.939594, 7.468337, 24.97506, -10.264824, 6.8368664, 24.649649, -10.304508, 6.7830296, 24.250587, -10.154926, 7.080727, 23.99825, -10.423819, 6.5334654, 24.740517, -10.394926, 6.530945, 24.408104, -10.264824, 6.8368664, 24.649649, -10.423819, 6.5334654, 24.740517, -10.448719, 6.45382, 24.497004, -10.394926, 6.530945, 24.408104, -10.423819, 6.5334654, 24.740517, -10.427696, 6.4519587, 25.310745, -10.554361, 6.258996, 24.674118, -10.647385, 5.933868, 25.242573, -10.554361, 6.258996, 24.674118, -10.427696, 6.4519587, 25.310745, -10.647385, 5.933868, 25.242573, -10.675802, 5.9358487, 24.97307, -10.554361, 6.258996, 24.674118, -8.476179, 9.272415, 23.238873, -7.6357913, 8.757383, 23.254288, -7.649511, 8.880784, 23.39443, -8.476179, 9.272415, 23.238873, -8.520136, 8.706246, 22.59189, -7.6357913, 8.757383, 23.254288, -8.663195, 8.882344, 23.984976, -8.29641, 9.024534, 23.588913, -8.32843, 8.858036, 23.843124, -8.663195, 8.882344, 23.984976, -8.476179, 9.272415, 23.238873, -8.29641, 9.024534, 23.588913, -12.152782, 7.7485704, 22.986092, -11.339409, 8.419413, 22.91118, -11.277941, 8.172669, 23.538235, -12.152782, 7.7485704, 22.986092, -12.161711, 7.7648354, 22.866318, -11.339409, 8.419413, 22.91118, -11.339409, 8.419413, 22.91118, -11.045532, 8.272723, 23.64941, -11.277941, 8.172669, 23.538235, -11.339409, 8.419413, 22.91118, -10.934807, 8.523428, 23.311256, -11.045532, 8.272723, 23.64941, -10.934807, 8.523428, 23.311256, -11.339409, 8.419413, 22.91118, -10.46002, 9.004967, 22.97543, -10.934807, 8.523428, 23.311256, -10.46002, 9.004967, 22.97543, -10.420095, 8.532848, 23.947876, -10.934807, 8.523428, 23.311256, -10.420095, 8.532848, 23.947876, -10.836201, 8.361673, 23.749374, -10.420095, 8.532848, 23.947876, -10.197205, 8.742559, 23.74126, -10.297283, 8.550915, 23.961588, -10.420095, 8.532848, 23.947876, -10.46002, 9.004967, 22.97543, -10.197205, 8.742559, 23.74126, -9.557905, 8.814163, 24.051355, -9.097107, 9.033176, 23.725506, -9.326338, 8.822926, 24.033926, -9.557905, 8.814163, 24.051355, -9.396812, 9.323492, 23.09922, -9.097107, 9.033176, 23.725506, -12.129438, 7.7163987, 22.76176, -11.339409, 8.419413, 22.91118, -12.161711, 7.7648354, 22.866318, -11.339409, 8.419413, 22.91118, -12.129438, 7.7163987, 22.76176, -11.6488285, 8.007675, 22.739779, -11.6488285, 8.007675, 22.739779, -12.129438, 7.7163987, 22.76176, -11.723551, 7.814352, 22.611177, -11.339409, 8.419413, 22.91118, -10.748026, 8.414791, 22.562681, -10.46002, 9.004967, 22.97543, -11.339409, 8.419413, 22.91118, -11.012053, 8.2395935, 22.52716, -10.748026, 8.414791, 22.562681, -10.238874, 8.305614, 22.187637, -9.396812, 9.323492, 23.09922, -10.46002, 9.004967, 22.97543, -9.396812, 9.323492, 23.09922, -10.238874, 8.305614, 22.187637, -9.780174, 8.406629, 22.240479, -9.396812, 9.323492, 23.09922, -9.780174, 8.406629, 22.240479, -9.642679, 8.753303, 22.532566, -8.943731, 9.065945, 22.87788, -8.476179, 9.272415, 23.238873, -9.396812, 9.323492, 23.09922, -8.476179, 9.272415, 23.238873, -8.943731, 9.065945, 22.87788, -8.7198925, 8.690582, 22.52244, -8.476179, 9.272415, 23.238873, -8.7198925, 8.690582, 22.52244, -8.520136, 8.706246, 22.59189, -9.396812, 9.323492, 23.09922, -10.197205, 8.742559, 23.74126, -10.46002, 9.004967, 22.97543, -9.730011, 9.053403, 23.47832, -10.197205, 8.742559, 23.74126, -9.396812, 9.323492, 23.09922, -10.197205, 8.742559, 23.74126, -9.730011, 9.053403, 23.47832, -9.879805, 8.6168785, 24.008417, -10.197205, 8.742559, 23.74126, -9.879805, 8.6168785, 24.008417, -10.089674, 8.597132, 23.985575, -9.396812, 9.323492, 23.09922, -9.784039, 8.756422, 24.024965, -9.730011, 9.053403, 23.47832, -9.396812, 9.323492, 23.09922, -9.557905, 8.814163, 24.051355, -9.784039, 8.756422, 24.024965, -9.642679, 8.753303, 22.532566, -9.374041, 8.548035, 22.28962, -9.396812, 9.323492, 23.09922, -9.642679, 8.753303, 22.532566, -9.570545, 8.498457, 22.26675, -9.374041, 8.548035, 22.28962, -8.476179, 9.272415, 23.238873, -9.097107, 9.033176, 23.725506, -9.396812, 9.323492, 23.09922, -9.097107, 9.033176, 23.725506, -8.476179, 9.272415, 23.238873, -8.663195, 8.882344, 23.984976, -9.097107, 9.033176, 23.725506, -8.663195, 8.882344, 23.984976, -8.990536, 8.872201, 24.00973, -8.943731, 9.065945, 22.87788, -9.374041, 8.548035, 22.28962, -8.84972, 8.556498, 22.469631, -8.943731, 9.065945, 22.87788, -9.396812, 9.323492, 23.09922, -9.374041, 8.548035, 22.28962, -7.649511, 8.880784, 23.39443, -8.29641, 9.024534, 23.588913, -8.476179, 9.272415, 23.238873, -8.29641, 9.024534, 23.588913, -7.649511, 8.880784, 23.39443, -7.667808, 8.801199, 23.562954, -8.29641, 9.024534, 23.588913, -7.667808, 8.801199, 23.562954, -8.106319, 8.869322, 23.749159, -10.748026, 8.414791, 22.562681, -10.238874, 8.305614, 22.187637, -10.46002, 9.004967, 22.97543, -10.748026, 8.414791, 22.562681, -10.583004, 8.204302, 22.268263, -10.238874, 8.305614, 22.187637, -11.012053, 8.2395935, 22.52716, -10.796698, 8.076697, 22.316998, -10.748026, 8.414791, 22.562681, -11.012053, 8.2395935, 22.52716, -10.918375, 8.067935, 22.346127, -10.796698, 8.076697, 22.316998, -11.012053, 8.2395935, 22.52716, -11.339409, 8.419413, 22.91118, -11.160143, 8.010557, 22.403072, -11.6488285, 8.007675, 22.739779, -11.160143, 8.010557, 22.403072, -11.339409, 8.419413, 22.91118, -11.6488285, 8.007675, 22.739779, -11.5230255, 7.8992805, 22.537394, -11.160143, 8.010557, 22.403072, -8.041105, 9.099136, 22.774368, -7.6169944, 8.80678, 23.443369, -7.7392535, 8.897409, 23.481745, -8.041105, 9.099136, 22.774368, -7.5259223, 8.667595, 22.517582, -7.6169944, 8.80678, 23.443369, -8.604433, 8.708588, 23.005474, -8.175547, 8.90155, 23.08837, -8.352665, 8.7482605, 23.189362, -8.604433, 8.708588, 23.005474, -8.041105, 9.099136, 22.774368, -8.175547, 8.90155, 23.08837, -9.354351, 7.2679915, 20.05077, -9.012537, 7.976405, 20.566238, -9.399052, 7.74749, 20.912569, -9.354351, 7.2679915, 20.05077, -9.275749, 7.2862973, 19.989244, -9.012537, 7.976405, 20.566238, -9.012537, 7.976405, 20.566238, -9.383741, 7.8629084, 21.12708, -9.399052, 7.74749, 20.912569, -9.012537, 7.976405, 20.566238, -9.122709, 8.10707, 21.038918, -9.383741, 7.8629084, 21.12708, -9.122709, 8.10707, 21.038918, -9.012537, 7.976405, 20.566238, -8.725763, 8.597912, 21.202042, -9.122709, 8.10707, 21.038918, -8.725763, 8.597912, 21.202042, -9.341703, 8.161026, 21.703133, -9.122709, 8.10707, 21.038918, -9.341703, 8.161026, 21.703133, -9.369871, 7.9649425, 21.320047, -9.341703, 8.161026, 21.703133, -9.114939, 8.379078, 21.764282, -9.299545, 8.194818, 21.797089, -9.341703, 8.161026, 21.703133, -8.725763, 8.597912, 21.202042, -9.114939, 8.379078, 21.764282, -9.049728, 8.516345, 22.375086, -8.632957, 8.781511, 22.557295, -8.934408, 8.559859, 22.53762, -9.049728, 8.516345, 22.375086, -8.364826, 9.012768, 22.024788, -8.632957, 8.781511, 22.557295, -9.180586, 7.258568, 19.97211, -9.012537, 7.976405, 20.566238, -9.275749, 7.2862973, 19.989244, -9.012537, 7.976405, 20.566238, -9.180586, 7.258568, 19.97211, -8.98185, 7.591978, 20.29632, -8.98185, 7.591978, 20.29632, -9.180586, 7.258568, 19.97211, -8.903774, 7.424042, 20.197647, -9.012537, 7.976405, 20.566238, -8.491125, 8.092604, 20.86079, -8.725763, 8.597912, 21.202042, -9.012537, 7.976405, 20.566238, -8.566002, 7.9071417, 20.66232, -8.491125, 8.092604, 20.86079, -7.9790645, 8.107249, 21.086702, -8.364826, 9.012768, 22.024788, -8.725763, 8.597912, 21.202042, -8.364826, 9.012768, 22.024788, -7.9790645, 8.107249, 21.086702, -7.822355, 8.259277, 21.4402, -8.364826, 9.012768, 22.024788, -7.822355, 8.259277, 21.4402, -8.002892, 8.544012, 21.647385, -7.964889, 8.880724, 22.295406, -8.041105, 9.099136, 22.774368, -8.364826, 9.012768, 22.024788, -8.041105, 9.099136, 22.774368, -7.964889, 8.880724, 22.295406, -7.5640388, 8.629003, 22.34164, -8.041105, 9.099136, 22.774368, -7.5640388, 8.629003, 22.34164, -7.5259223, 8.667595, 22.517582, -8.364826, 9.012768, 22.024788, -9.114939, 8.379078, 21.764282, -8.725763, 8.597912, 21.202042, -8.749628, 8.718971, 21.97248, -9.114939, 8.379078, 21.764282, -8.364826, 9.012768, 22.024788, -9.114939, 8.379078, 21.764282, -8.749628, 8.718971, 21.97248, -9.156193, 8.312938, 22.116726, -9.114939, 8.379078, 21.764282, -9.156193, 8.312938, 22.116726, -9.228645, 8.264259, 21.957209, -8.364826, 9.012768, 22.024788, -9.126802, 8.436518, 22.200216, -8.749628, 8.718971, 21.97248, -8.364826, 9.012768, 22.024788, -9.049728, 8.516345, 22.375086, -9.126802, 8.436518, 22.200216, -8.002892, 8.544012, 21.647385, -7.684996, 8.432556, 21.75747, -8.364826, 9.012768, 22.024788, -8.002892, 8.544012, 21.647385, -7.7519794, 8.363534, 21.605556, -7.684996, 8.432556, 21.75747, -8.041105, 9.099136, 22.774368, -8.632957, 8.781511, 22.557295, -8.364826, 9.012768, 22.024788, -8.632957, 8.781511, 22.557295, -8.041105, 9.099136, 22.774368, -8.604433, 8.708588, 23.005474, -8.632957, 8.781511, 22.557295, -8.604433, 8.708588, 23.005474, -8.767645, 8.649889, 22.776098, -7.964889, 8.880724, 22.295406, -7.684996, 8.432556, 21.75747, -7.5840287, 8.511244, 22.2162, -7.964889, 8.880724, 22.295406, -8.364826, 9.012768, 22.024788, -7.684996, 8.432556, 21.75747, -7.7392535, 8.897409, 23.481745, -8.175547, 8.90155, 23.08837, -8.041105, 9.099136, 22.774368, -8.175547, 8.90155, 23.08837, -7.7392535, 8.897409, 23.481745, -7.856048, 8.8193245, 23.551592, -8.175547, 8.90155, 23.08837, -7.856048, 8.8193245, 23.551592, -8.1856785, 8.794295, 23.313171, -8.491125, 8.092604, 20.86079, -7.9790645, 8.107249, 21.086702, -8.725763, 8.597912, 21.202042, -8.491125, 8.092604, 20.86079, -8.181248, 7.964281, 20.873774, -7.9790645, 8.107249, 21.086702, -8.566002, 7.9071417, 20.66232, -8.305813, 7.8208942, 20.736774, -8.491125, 8.092604, 20.86079, -8.566002, 7.9071417, 20.66232, -8.377768, 7.7919035, 20.663399, -8.305813, 7.8208942, 20.736774, -8.566002, 7.9071417, 20.66232, -9.012537, 7.976405, 20.566238, -8.519827, 7.7004347, 20.51477, -8.98185, 7.591978, 20.29632, -8.519827, 7.7004347, 20.51477, -9.012537, 7.976405, 20.566238, -8.98185, 7.591978, 20.29632, -8.76738, 7.536939, 20.311623, -8.519827, 7.7004347, 20.51477, -7.2060475, 9.208552, 22.897741, -7.80905, 8.817884, 23.60042, -7.899529, 8.892069, 23.45427, -7.2060475, 9.208552, 22.897741, -6.7061973, 8.855337, 23.494766, -7.80905, 8.817884, 23.60042, -7.4934087, 8.594069, 22.34334, -7.539685, 8.893689, 22.84897, -7.65863, 8.6545105, 22.680805, -7.4934087, 8.594069, 22.34334, -7.2060475, 9.208552, 22.897741, -7.539685, 8.893689, 22.84897, -4.068832, 7.336172, 20.780975, -4.714312, 8.10803, 21.257643, -5.132072, 7.6965923, 20.921396, -4.068832, 7.336172, 20.780975, -3.9881473, 7.385569, 20.85462, -4.714312, 8.10803, 21.257643, -4.714312, 8.10803, 21.257643, -5.3909154, 7.790823, 20.987743, -5.132072, 7.6965923, 20.921396, -4.714312, 8.10803, 21.257643, -5.2879725, 8.145002, 21.248096, -5.3909154, 7.790823, 20.987743, -5.2879725, 8.145002, 21.248096, -4.714312, 8.10803, 21.257643, -5.489513, 8.767287, 21.711481, -5.2879725, 8.145002, 21.248096, -5.489513, 8.767287, 21.711481, -6.085754, 8.035226, 21.16589, -5.2879725, 8.145002, 21.248096, -6.085754, 8.035226, 21.16589, -5.623652, 7.8744917, 21.047535, -6.085754, 8.035226, 21.16589, -6.1478257, 8.328001, 21.433554, -6.1905837, 8.063794, 21.235548, -6.085754, 8.035226, 21.16589, -5.489513, 8.767287, 21.711481, -6.1478257, 8.328001, 21.433554, -6.841514, 8.391562, 21.667154, -7.0118194, 8.758585, 22.183632, -7.009152, 8.4349575, 21.841381, -6.841514, 8.391562, 21.667154, -6.416242, 9.177944, 22.323563, -7.0118194, 8.758585, 22.183632, -3.9419742, 7.374406, 20.966194, -4.714312, 8.10803, 21.257643, -3.9881473, 7.385569, 20.85462, -4.714312, 8.10803, 21.257643, -3.9419742, 7.374406, 20.966194, -4.32668, 7.7234807, 21.258965, -4.32668, 7.7234807, 21.258965, -3.9419742, 7.374406, 20.966194, -4.1686845, 7.564969, 21.337112, -4.714312, 8.10803, 21.257643, -4.9478, 8.286828, 21.951738, -5.489513, 8.767287, 21.711481, -4.714312, 8.10803, 21.257643, -4.704924, 8.094104, 21.826122, -4.9478, 8.286828, 21.951738, -5.0908885, 8.360533, 22.620289, -6.416242, 9.177944, 22.323563, -5.489513, 8.767287, 21.711481, -6.416242, 9.177944, 22.323563, -5.0908885, 8.360533, 22.620289, -5.486716, 8.50116, 22.883144, -6.416242, 9.177944, 22.323563, -5.486716, 8.50116, 22.883144, -5.8117423, 8.768128, 22.703707, -6.5980697, 9.059402, 22.89238, -7.2060475, 9.208552, 22.897741, -6.416242, 9.177944, 22.323563, -7.2060475, 9.208552, 22.897741, -6.5980697, 9.059402, 22.89238, -6.507363, 8.83523, 23.408497, -7.2060475, 9.208552, 22.897741, -6.507363, 8.83523, 23.408497, -6.7061973, 8.855337, 23.494766, -6.416242, 9.177944, 22.323563, -6.1478257, 8.328001, 21.433554, -5.489513, 8.767287, 21.711481, -6.3719687, 8.766026, 21.89067, -6.1478257, 8.328001, 21.433554, -6.416242, 9.177944, 22.323563, -6.1478257, 8.328001, 21.433554, -6.3719687, 8.766026, 21.89067, -6.5476384, 8.165707, 21.472878, -6.1478257, 8.328001, 21.433554, -6.5476384, 8.165707, 21.472878, -6.369827, 8.1280155, 21.354618, -6.416242, 9.177944, 22.323563, -6.645247, 8.314198, 21.536797, -6.3719687, 8.766026, 21.89067, -6.416242, 9.177944, 22.323563, -6.841514, 8.391562, 21.667154, -6.645247, 8.314198, 21.536797, -5.8117423, 8.768128, 22.703707, -5.8438954, 8.677977, 23.119993, -6.416242, 9.177944, 22.323563, -5.8117423, 8.768128, 22.703707, -5.6736007, 8.611357, 23.006952, -5.8438954, 8.677977, 23.119993, -7.2060475, 9.208552, 22.897741, -7.0118194, 8.758585, 22.183632, -6.416242, 9.177944, 22.323563, -7.0118194, 8.758585, 22.183632, -7.2060475, 9.208552, 22.897741, -7.4934087, 8.594069, 22.34334, -7.0118194, 8.758585, 22.183632, -7.4934087, 8.594069, 22.34334, -7.256623, 8.534951, 22.097307, -6.5980697, 9.059402, 22.89238, -5.8438954, 8.677977, 23.119993, -6.361113, 8.698025, 23.343914, -6.5980697, 9.059402, 22.89238, -6.416242, 9.177944, 22.323563, -5.8438954, 8.677977, 23.119993, -7.899529, 8.892069, 23.45427, -7.539685, 8.893689, 22.84897, -7.2060475, 9.208552, 22.897741, -7.539685, 8.893689, 22.84897, -7.899529, 8.892069, 23.45427, -7.983706, 8.764886, 23.345701, -7.539685, 8.893689, 22.84897, -7.983706, 8.764886, 23.345701, -7.7710595, 8.722573, 22.907578, -4.9478, 8.286828, 21.951738, -5.0908885, 8.360533, 22.620289, -5.489513, 8.767287, 21.711481, -4.9478, 8.286828, 21.951738, -4.867348, 8.1940365, 22.336489, -5.0908885, 8.360533, 22.620289, -4.704924, 8.094104, 21.826122, -4.7211885, 8.02508, 22.154125, -4.9478, 8.286828, 21.951738, -4.704924, 8.094104, 21.826122, -4.6452146, 7.9936295, 22.056368, -4.7211885, 8.02508, 22.154125, -4.704924, 8.094104, 21.826122, -4.714312, 8.10803, 21.257643, -4.489761, 7.8906374, 21.858315, -4.32668, 7.7234807, 21.258965, -4.489761, 7.8906374, 21.858315, -4.714312, 8.10803, 21.257643, -4.32668, 7.7234807, 21.258965, -4.284569, 7.6962314, 21.524137, -4.489761, 7.8906374, 21.858315, -7.4770913, 9.070688, 24.137522, -7.9792457, 8.86656, 23.280897, -7.798713, 8.910673, 23.258356, -7.4770913, 9.070688, 24.137522, -8.267395, 8.83601, 24.344084, -7.9792457, 8.86656, 23.280897, -6.969793, 8.361493, 23.999329, -7.3750834, 8.766386, 23.809656, -7.2131476, 8.500739, 23.731937, -6.969793, 8.361493, 23.999329, -7.4770913, 9.070688, 24.137522, -7.3750834, 8.766386, 23.809656, -6.8215938, 6.6485844, 27.584244, -6.934275, 7.533578, 26.905539, -6.54954, 7.0755653, 26.591766, -6.8215938, 6.6485844, 27.584244, -6.908404, 6.709503, 27.638208, -6.934275, 7.533578, 26.905539, -6.934275, 7.533578, 26.905539, -6.512714, 7.1929045, 26.339361, -6.54954, 7.0755653, 26.591766, -6.934275, 7.533578, 26.905539, -6.735351, 7.5916777, 26.38232, -6.512714, 7.1929045, 26.339361, -6.735351, 7.5916777, 26.38232, -6.934275, 7.533578, 26.905539, -7.004352, 8.309274, 26.101746, -6.735351, 7.5916777, 26.38232, -7.004352, 8.309274, 26.101746, -6.413199, 7.4994855, 25.660645, -6.735351, 7.5916777, 26.38232, -6.413199, 7.4994855, 25.660645, -6.4796042, 7.2974, 26.112244, -6.413199, 7.4994855, 25.660645, -6.6031613, 7.8468814, 25.541204, -6.4362464, 7.5467825, 25.541874, -6.413199, 7.4994855, 25.660645, -7.004352, 8.309274, 26.101746, -6.6031613, 7.8468814, 25.541204, -6.5875335, 7.9890084, 24.822481, -6.9586644, 8.467667, 24.52183, -6.6856866, 8.076338, 24.609621, -6.5875335, 7.9890084, 24.822481, -7.210817, 8.882824, 25.071764, -6.9586644, 8.467667, 24.52183, -7.030128, 6.721147, 27.639519, -6.934275, 7.533578, 26.905539, -6.908404, 6.709503, 27.638208, -6.934275, 7.533578, 26.905539, -7.030128, 6.721147, 27.639519, -7.122934, 7.142128, 27.21883, -7.122934, 7.142128, 27.21883, -7.030128, 6.721147, 27.639519, -7.2680945, 6.9975395, 27.31846, -6.934275, 7.533578, 26.905539, -7.48521, 7.8700495, 26.46049, -7.004352, 8.309274, 26.101746, -6.934275, 7.533578, 26.905539, -7.476253, 7.6441336, 26.707956, -7.48521, 7.8700495, 26.46049, -8.052195, 8.094164, 26.095999, -7.210817, 8.882824, 25.071764, -7.004352, 8.309274, 26.101746, -7.210817, 8.882824, 25.071764, -8.052195, 8.094164, 26.095999, -8.141077, 8.305193, 25.651417, -7.210817, 8.882824, 25.071764, -8.141077, 8.305193, 25.651417, -7.8344674, 8.5407715, 25.446497, -7.7096314, 8.899331, 24.684853, -7.4770913, 9.070688, 24.137522, -7.210817, 8.882824, 25.071764, -7.4770913, 9.070688, 24.137522, -7.7096314, 8.899331, 24.684853, -8.258791, 8.789434, 24.556768, -7.4770913, 9.070688, 24.137522, -8.258791, 8.789434, 24.556768, -8.267395, 8.83601, 24.344084, -7.210817, 8.882824, 25.071764, -6.6031613, 7.8468814, 25.541204, -7.004352, 8.309274, 26.101746, -6.8924146, 8.3843, 25.220247, -6.6031613, 7.8468814, 25.541204, -7.210817, 8.882824, 25.071764, -6.6031613, 7.8468814, 25.541204, -6.8924146, 8.3843, 25.220247, -6.5150523, 7.712496, 25.137945, -6.6031613, 7.8468814, 25.541204, -6.5150523, 7.712496, 25.137945, -6.4764667, 7.6428747, 25.340647, -7.210817, 8.882824, 25.071764, -6.543115, 7.8768907, 25.041906, -6.8924146, 8.3843, 25.220247, -7.210817, 8.882824, 25.071764, -6.5875335, 7.9890084, 24.822481, -6.543115, 7.8768907, 25.041906, -7.8344674, 8.5407715, 25.446497, -8.223946, 8.544853, 25.256237, -7.210817, 8.882824, 25.071764, -7.8344674, 8.5407715, 25.446497, -8.185364, 8.448042, 25.446867, -8.223946, 8.544853, 25.256237, -7.4770913, 9.070688, 24.137522, -6.9586644, 8.467667, 24.52183, -7.210817, 8.882824, 25.071764, -6.9586644, 8.467667, 24.52183, -7.4770913, 9.070688, 24.137522, -6.969793, 8.361493, 23.999329, -6.9586644, 8.467667, 24.52183, -6.969793, 8.361493, 23.999329, -6.8314404, 8.240372, 24.300121, -7.7096314, 8.899331, 24.684853, -8.223946, 8.544853, 25.256237, -8.244255, 8.633982, 24.69929, -7.7096314, 8.899331, 24.684853, -7.210817, 8.882824, 25.071764, -8.223946, 8.544853, 25.256237, -7.798713, 8.910673, 23.258356, -7.3750834, 8.766386, 23.809656, -7.4770913, 9.070688, 24.137522, -7.3750834, 8.766386, 23.809656, -7.798713, 8.910673, 23.258356, -7.6923747, 8.766327, 23.204716, -7.3750834, 8.766386, 23.809656, -7.6923747, 8.766327, 23.204716, -7.3779397, 8.62144, 23.55421, -7.48521, 7.8700495, 26.46049, -8.052195, 8.094164, 26.095999, -7.004352, 8.309274, 26.101746, -7.48521, 7.8700495, 26.46049, -7.886131, 7.860207, 26.385628, -8.052195, 8.094164, 26.095999, -7.476253, 7.6441336, 26.707956, -7.776336, 7.6484556, 26.566654, -7.48521, 7.8700495, 26.46049, -7.476253, 7.6441336, 26.707956, -7.720366, 7.5934772, 26.668566, -7.776336, 7.6484556, 26.566654, -7.476253, 7.6441336, 26.707956, -6.934275, 7.533578, 26.905539, -7.604976, 7.443248, 26.871801, -7.122934, 7.142128, 27.21883, -7.604976, 7.443248, 26.871801, -6.934275, 7.533578, 26.905539, -7.122934, 7.142128, 27.21883, -7.3896413, 7.171777, 27.159313, -7.604976, 7.443248, 26.871801, -6.0571437, 2.278272, 22.798777, -6.422816, 4.561778, 23.067327, -6.255482, 4.6069903, 22.866049, -6.0571437, 2.278272, 22.798777, -6.2640643, 2.2363775, 23.040073, -6.422816, 4.561778, 23.067327, -6.229696, -0.29725552, 22.954578, -6.2640643, 2.2363775, 23.040073, -6.0571437, 2.278272, 22.798777, -6.229696, -0.29725552, 22.954578, -6.4595838, -0.294353, 23.230642, -6.2640643, 2.2363775, 23.040073, -6.8122897, 2.1787193, 22.89551, -6.9267097, 4.515596, 22.675344, -6.886102, 4.499142, 22.937313, -6.8122897, 2.1787193, 22.89551, -6.8565516, 2.1943433, 22.589626, -6.9267097, 4.515596, 22.675344, -6.34096, 2.278272, 22.29763, -6.2974906, 4.6231685, 22.603962, -6.5006537, 4.606991, 22.43314, -6.34096, 2.278272, 22.29763, -6.0964046, 2.2940338, 22.49008, -6.2974906, 4.6231685, 22.603962, -6.886102, 4.499142, 22.937313, -7.330835, 6.6817102, 22.999025, -7.3017764, 6.6674695, 23.172724, -6.886102, 4.499142, 22.937313, -6.9267097, 4.515596, 22.675344, -7.330835, 6.6817102, 22.999025, -6.255482, 4.6069903, 22.866049, -6.9959083, 6.7194586, 23.261988, -6.883434, 6.7554083, 23.12764, -6.255482, 4.6069903, 22.866049, -6.422816, 4.561778, 23.067327, -6.9959083, 6.7194586, 23.261988, -6.883434, 6.7554083, 23.12764, -6.9959083, 6.7194586, 23.261988, -7.3726697, 7.8587894, 23.408417, -6.883434, 6.7554083, 23.12764, -7.3726697, 7.8587894, 23.408417, -7.2935386, 7.8998566, 23.310549, -6.2640643, 2.2363775, 23.040073, -6.6823287, 4.515596, 23.106857, -6.422816, 4.561778, 23.067327, -6.2640643, 2.2363775, 23.040073, -6.5727344, 2.1943433, 23.090773, -6.6823287, 4.515596, 23.106857, -6.422816, 4.561778, 23.067327, -6.6823287, 4.515596, 23.106857, -7.1677566, 6.6817102, 23.28698, -6.422816, 4.561778, 23.067327, -7.1677566, 6.6817102, 23.28698, -6.9959083, 6.7194586, 23.261988, -6.8565516, 2.1943433, 22.589626, -6.759332, 4.561778, 22.473125, -6.9267097, 4.515596, 22.675344, -6.8565516, 2.1943433, 22.589626, -6.6543117, 2.2363775, 22.350998, -6.759332, 4.561778, 22.473125, -6.9267097, 4.515596, 22.675344, -6.759332, 4.561778, 22.473125, -7.2210193, 6.7194586, 22.864502, -6.9267097, 4.515596, 22.675344, -7.2210193, 6.7194586, 22.864502, -7.330835, 6.6817102, 22.999025, -6.5006537, 4.606991, 22.43314, -6.9114146, 6.7684054, 22.951649, -7.0479617, 6.7554083, 22.837128, -6.5006537, 4.606991, 22.43314, -6.2974906, 4.6231685, 22.603962, -6.9114146, 6.7684054, 22.951649, -6.0964046, 2.2940338, 22.49008, -6.255482, 4.6069903, 22.866049, -6.2974906, 4.6231685, 22.603962, -6.0964046, 2.2940338, 22.49008, -6.0571437, 2.278272, 22.798777, -6.255482, 4.6069903, 22.866049, -6.2974906, 4.6231685, 22.603962, -6.883434, 6.7554083, 23.12764, -6.9114146, 6.7684054, 22.951649, -6.2974906, 4.6231685, 22.603962, -6.255482, 4.6069903, 22.866049, -6.883434, 6.7554083, 23.12764, -6.6543117, 2.2363775, 22.350998, -6.5006537, 4.606991, 22.43314, -6.759332, 4.561778, 22.473125, -6.6543117, 2.2363775, 22.350998, -6.34096, 2.278272, 22.29763, -6.5006537, 4.606991, 22.43314, -6.9183087, -0.294353, 22.420652, -6.34096, 2.278272, 22.29763, -6.6543117, 2.2363775, 22.350998, -6.9183087, -0.294353, 22.420652, -6.563316, -0.29725552, 22.365494, -6.34096, 2.278272, 22.29763, -6.759332, 4.561778, 22.473125, -7.0479617, 6.7554083, 22.837128, -7.2210193, 6.7194586, 22.864502, -6.759332, 4.561778, 22.473125, -6.5006537, 4.606991, 22.43314, -7.0479617, 6.7554083, 22.837128, -7.2210193, 6.7194586, 22.864502, -7.0479617, 6.7554083, 22.837128, -7.4157386, 7.8998566, 23.094776, -7.2210193, 6.7194586, 22.864502, -7.4157386, 7.8998566, 23.094776, -7.5403686, 7.8587894, 23.112305, -7.596041, 7.8030686, 23.339376, -7.8122087, 8.670566, 23.565685, -7.4973254, 7.8185544, 23.425323, -7.596041, 7.8030686, 23.339376, -7.884003, 8.657431, 23.502459, -7.8122087, 8.670566, 23.565685, -7.3726697, 7.8587894, 23.408417, -7.6698213, 8.747442, 23.485046, -7.2935386, 7.8998566, 23.310549, -7.3726697, 7.8587894, 23.408417, -7.724811, 8.710249, 23.555187, -7.6698213, 8.747442, 23.485046, -7.4973254, 7.8185544, 23.425323, -7.724811, 8.710249, 23.555187, -7.3726697, 7.8587894, 23.408417, -7.4973254, 7.8185544, 23.425323, -7.8122087, 8.670566, 23.565685, -7.724811, 8.710249, 23.555187, -7.3161235, 7.9149265, 23.18085, -7.7589135, 8.747442, 23.327732, -7.4157386, 7.8998566, 23.094776, -7.3161235, 7.9149265, 23.18085, -7.6881547, 8.763065, 23.391544, -7.7589135, 8.747442, 23.327732, -7.2935386, 7.8998566, 23.310549, -7.6881547, 8.763065, 23.391544, -7.3161235, 7.9149265, 23.18085, -7.2935386, 7.8998566, 23.310549, -7.6698213, 8.747442, 23.485046, -7.6881547, 8.763065, 23.391544, -7.5403686, 7.8587894, 23.112305, -7.901301, 8.670566, 23.408371, -7.61898, 7.8185544, 23.210514, -7.5403686, 7.8587894, 23.112305, -7.8473487, 8.710249, 23.338818, -7.901301, 8.670566, 23.408371, -7.4157386, 7.8998566, 23.094776, -7.8473487, 8.710249, 23.338818, -7.5403686, 7.8587894, 23.112305, -7.4157386, 7.8998566, 23.094776, -7.7589135, 8.747442, 23.327732, -7.8473487, 8.710249, 23.338818, -6.8164535, -0.2914486, 23.28688, -6.8122897, 2.1787193, 22.89551, -6.5727344, 2.1943433, 23.090773, -6.8164535, -0.2914486, 23.28688, -7.092834, -0.29034328, 23.054392, -6.8122897, 2.1787193, 22.89551, -7.61898, 7.8185544, 23.210514, -7.884003, 8.657431, 23.502459, -7.596041, 7.8030686, 23.339376, -7.61898, 7.8185544, 23.210514, -7.901301, 8.670566, 23.408371, -7.884003, 8.657431, 23.502459, -6.5727344, 2.1943433, 23.090773, -6.886102, 4.499142, 22.937313, -6.6823287, 4.515596, 23.106857, -6.5727344, 2.1943433, 23.090773, -6.8122897, 2.1787193, 22.89551, -6.886102, 4.499142, 22.937313, -7.092834, -0.29034328, 23.054392, -6.8565516, 2.1943433, 22.589626, -6.8122897, 2.1787193, 22.89551, -7.092834, -0.29034328, 23.054392, -7.1500726, -0.2914486, 22.697796, -6.8565516, 2.1943433, 22.589626, -6.563316, -0.29725552, 22.365494, -6.0964046, 2.2940338, 22.49008, -6.34096, 2.278272, 22.29763, -6.563316, -0.29725552, 22.365494, -6.288299, -0.2983632, 22.598755, -6.0964046, 2.2940338, 22.49008, -6.4595838, -0.294353, 23.230642, -6.5727344, 2.1943433, 23.090773, -6.2640643, 2.2363775, 23.040073, -6.4595838, -0.294353, 23.230642, -6.8164535, -0.2914486, 23.28688, -6.5727344, 2.1943433, 23.090773, -7.1500726, -0.2914486, 22.697796, -6.6543117, 2.2363775, 22.350998, -6.8565516, 2.1943433, 22.589626, -7.1500726, -0.2914486, 22.697796, -6.9183087, -0.294353, 22.420652, -6.6543117, 2.2363775, 22.350998, -6.288299, -0.2983632, 22.598755, -6.0571437, 2.278272, 22.798777, -6.0964046, 2.2940338, 22.49008, -6.288299, -0.2983632, 22.598755, -6.229696, -0.29725552, 22.954578, -6.0571437, 2.278272, 22.798777, -6.6823287, 4.515596, 23.106857, -7.3017764, 6.6674695, 23.172724, -7.1677566, 6.6817102, 23.28698, -6.6823287, 4.515596, 23.106857, -6.886102, 4.499142, 22.937313, -7.3017764, 6.6674695, 23.172724, -7.1677566, 6.6817102, 23.28698, -7.596041, 7.8030686, 23.339376, -7.4973254, 7.8185544, 23.425323, -7.1677566, 6.6817102, 23.28698, -7.3017764, 6.6674695, 23.172724, -7.596041, 7.8030686, 23.339376, -7.3017764, 6.6674695, 23.172724, -7.61898, 7.8185544, 23.210514, -7.596041, 7.8030686, 23.339376, -7.3017764, 6.6674695, 23.172724, -7.330835, 6.6817102, 22.999025, -7.61898, 7.8185544, 23.210514, -6.9959083, 6.7194586, 23.261988, -7.4973254, 7.8185544, 23.425323, -7.3726697, 7.8587894, 23.408417, -6.9959083, 6.7194586, 23.261988, -7.1677566, 6.6817102, 23.28698, -7.4973254, 7.8185544, 23.425323, -7.0479617, 6.7554083, 22.837128, -7.3161235, 7.9149265, 23.18085, -7.4157386, 7.8998566, 23.094776, -7.0479617, 6.7554083, 22.837128, -6.9114146, 6.7684054, 22.951649, -7.3161235, 7.9149265, 23.18085, -6.9114146, 6.7684054, 22.951649, -7.2935386, 7.8998566, 23.310549, -7.3161235, 7.9149265, 23.18085, -6.9114146, 6.7684054, 22.951649, -6.883434, 6.7554083, 23.12764, -7.2935386, 7.8998566, 23.310549, -7.330835, 6.6817102, 22.999025, -7.5403686, 7.8587894, 23.112305, -7.61898, 7.8185544, 23.210514, -7.330835, 6.6817102, 22.999025, -7.2210193, 6.7194586, 22.864502, -7.5403686, 7.8587894, 23.112305, -5.520864, 5.50994, 24.639458, -6.0238147, 5.3727474, 24.921116, -6.027886, 5.4678555, 24.868423, -5.520864, 5.50994, 24.639458, -5.4553275, 5.043026, 24.83633, -6.0238147, 5.3727474, 24.921116, -5.780359, 5.49264, 24.220936, -5.780785, 5.505308, 24.545547, -5.885944, 5.489472, 24.411573, -5.780359, 5.49264, 24.220936, -5.520864, 5.50994, 24.639458, -5.780785, 5.505308, 24.545547, -4.172077, 4.097315, 23.081501, -4.3400836, 4.5620866, 23.516861, -4.6323843, 4.612814, 23.259392, -4.172077, 4.097315, 23.081501, -4.1252418, 4.0723934, 23.12863, -4.3400836, 4.5620866, 23.516861, -4.3400836, 4.5620866, 23.516861, -4.7473187, 4.731488, 23.319653, -4.6323843, 4.612814, 23.259392, -4.3400836, 4.5620866, 23.516861, -4.6260543, 4.7886457, 23.530365, -4.7473187, 4.731488, 23.319653, -4.6260543, 4.7886457, 23.530365, -4.3400836, 4.5620866, 23.516861, -4.6092978, 5.019003, 23.91192, -4.6260543, 4.7886457, 23.530365, -4.6092978, 5.019003, 23.91192, -5.05752, 5.0416813, 23.479633, -4.6260543, 4.7886457, 23.530365, -5.05752, 5.0416813, 23.479633, -4.8509307, 4.836937, 23.373531, -5.05752, 5.0416813, 23.479633, -5.044219, 5.1299534, 23.679413, -5.109679, 5.073374, 23.52538, -5.05752, 5.0416813, 23.479633, -4.6092978, 5.019003, 23.91192, -5.044219, 5.1299534, 23.679413, -5.416506, 5.348407, 23.822437, -5.4675636, 5.4387703, 24.16735, -5.5109153, 5.382139, 23.924562, -5.416506, 5.348407, 23.822437, -5.0510015, 5.370203, 24.32837, -5.4675636, 5.4387703, 24.16735, -4.119844, 4.0247717, 23.180462, -4.3400836, 4.5620866, 23.516861, -4.1252418, 4.0723934, 23.12863, -4.3400836, 4.5620866, 23.516861, -4.119844, 4.0247717, 23.180462, -4.253202, 4.254257, 23.418776, -4.253202, 4.254257, 23.418776, -4.119844, 4.0247717, 23.180462, -4.22462, 4.107806, 23.420288, -4.3400836, 4.5620866, 23.516861, -4.504075, 4.5656476, 23.906963, -4.6092978, 5.019003, 23.91192, -4.3400836, 4.5620866, 23.516861, -4.4175224, 4.424284, 23.794865, -4.504075, 4.5656476, 23.906963, -4.644364, 4.490939, 24.260977, -5.0510015, 5.370203, 24.32837, -4.6092978, 5.019003, 23.91192, -5.0510015, 5.370203, 24.32837, -4.644364, 4.490939, 24.260977, -4.8376765, 4.627639, 24.436502, -5.0510015, 5.370203, 24.32837, -4.8376765, 4.627639, 24.436502, -4.9126344, 4.897375, 24.407942, -5.261839, 5.2418065, 24.581022, -5.520864, 5.50994, 24.639458, -5.0510015, 5.370203, 24.32837, -5.520864, 5.50994, 24.639458, -5.261839, 5.2418065, 24.581022, -5.3500004, 4.9882493, 24.782772, -5.520864, 5.50994, 24.639458, -5.3500004, 4.9882493, 24.782772, -5.4553275, 5.043026, 24.83633, -5.0510015, 5.370203, 24.32837, -5.044219, 5.1299534, 23.679413, -4.6092978, 5.019003, 23.91192, -5.09594, 5.2896347, 24.011734, -5.044219, 5.1299534, 23.679413, -5.0510015, 5.370203, 24.32837, -5.044219, 5.1299534, 23.679413, -5.09594, 5.2896347, 24.011734, -5.2869577, 5.1833105, 23.681408, -5.044219, 5.1299534, 23.679413, -5.2869577, 5.1833105, 23.681408, -5.197224, 5.1352034, 23.604895, -5.0510015, 5.370203, 24.32837, -5.321176, 5.2789783, 23.735258, -5.09594, 5.2896347, 24.011734, -5.0510015, 5.370203, 24.32837, -5.416506, 5.348407, 23.822437, -5.321176, 5.2789783, 23.735258, -4.9126344, 4.897375, 24.407942, -5.007563, 4.7688026, 24.597364, -5.0510015, 5.370203, 24.32837, -4.9126344, 4.897375, 24.407942, -4.924829, 4.7089424, 24.521822, -5.007563, 4.7688026, 24.597364, -5.520864, 5.50994, 24.639458, -5.4675636, 5.4387703, 24.16735, -5.0510015, 5.370203, 24.32837, -5.4675636, 5.4387703, 24.16735, -5.520864, 5.50994, 24.639458, -5.780359, 5.49264, 24.220936, -5.4675636, 5.4387703, 24.16735, -5.780359, 5.49264, 24.220936, -5.6462617, 5.4495068, 24.077614, -5.261839, 5.2418065, 24.581022, -5.007563, 4.7688026, 24.597364, -5.284591, 4.8992395, 24.734537, -5.261839, 5.2418065, 24.581022, -5.0510015, 5.370203, 24.32837, -5.007563, 4.7688026, 24.597364, -6.027886, 5.4678555, 24.868423, -5.780785, 5.505308, 24.545547, -5.520864, 5.50994, 24.639458, -5.780785, 5.505308, 24.545547, -6.027886, 5.4678555, 24.868423, -6.0964007, 5.467935, 24.783894, -5.780785, 5.505308, 24.545547, -6.0964007, 5.467935, 24.783894, -5.9559407, 5.492031, 24.540054, -4.504075, 4.5656476, 23.906963, -4.644364, 4.490939, 24.260977, -4.6092978, 5.019003, 23.91192, -4.504075, 4.5656476, 23.906963, -4.5401545, 4.407131, 24.07597, -4.644364, 4.490939, 24.260977, -4.4175224, 4.424284, 23.794865, -4.4788713, 4.3216662, 23.951574, -4.504075, 4.5656476, 23.906963, -4.4175224, 4.424284, 23.794865, -4.4406796, 4.305605, 23.890078, -4.4788713, 4.3216662, 23.951574, -4.4175224, 4.424284, 23.794865, -4.3400836, 4.5620866, 23.516861, -4.367128, 4.250943, 23.76148, -4.253202, 4.254257, 23.418776, -4.367128, 4.250943, 23.76148, -4.3400836, 4.5620866, 23.516861, -4.253202, 4.254257, 23.418776, -4.274289, 4.1678133, 23.544254, -4.367128, 4.250943, 23.76148, -6.100014, 5.3618755, 25.36038, -6.053468, 5.4692774, 24.699776, -5.951238, 5.4974275, 24.761002, -6.100014, 5.3618755, 25.36038, -6.5840898, 5.185566, 25.139408, -6.053468, 5.4692774, 24.699776, -5.702927, 4.9555445, 25.310673, -5.8909087, 5.2601395, 25.158152, -5.754765, 5.1142435, 25.118452, -5.702927, 4.9555445, 25.310673, -6.100014, 5.3618755, 25.36038, -5.8909087, 5.2601395, 25.158152, -6.868545, 2.974114, 26.87804, -6.7395024, 3.6925054, 26.67168, -6.369455, 3.4866214, 26.543037, -6.868545, 2.974114, 26.87804, -6.9426017, 2.9990473, 26.889389, -6.7395024, 3.6925054, 26.67168, -6.7395024, 3.6925054, 26.67168, -6.2608256, 3.6231003, 26.448338, -6.369455, 3.4866214, 26.543037, -6.7395024, 3.6925054, 26.67168, -6.429816, 3.8601167, 26.47786, -6.2608256, 3.6231003, 26.448338, -6.429816, 3.8601167, 26.47786, -6.7395024, 3.6925054, 26.67168, -6.5268407, 4.3764234, 26.390133, -6.429816, 3.8601167, 26.47786, -6.5268407, 4.3764234, 26.390133, -5.9696584, 3.9842505, 26.192686, -6.429816, 3.8601167, 26.47786, -5.9696584, 3.9842505, 26.192686, -6.1631923, 3.745168, 26.363018, -5.9696584, 3.9842505, 26.192686, -6.049317, 4.232349, 26.135853, -5.944036, 4.043989, 26.133595, -5.9696584, 3.9842505, 26.192686, -6.5268407, 4.3764234, 26.390133, -6.049317, 4.232349, 26.135853, -5.773127, 4.5062346, 25.794031, -5.898379, 4.8849564, 25.608694, -5.7558064, 4.6165876, 25.667988, -5.773127, 4.5062346, 25.794031, -6.2885137, 4.9992027, 25.899937, -5.898379, 4.8849564, 25.608694, -7.0106807, 3.0087154, 26.849985, -6.7395024, 3.6925054, 26.67168, -6.9426017, 2.9990473, 26.889389, -6.7395024, 3.6925054, 26.67168, -7.0106807, 3.0087154, 26.849985, -6.9334645, 3.3768253, 26.686901, -6.9334645, 3.3768253, 26.686901, -7.0106807, 3.0087154, 26.849985, -7.0455823, 3.2654228, 26.659414, -6.7395024, 3.6925054, 26.67168, -6.892614, 4.0271807, 26.316252, -6.5268407, 4.3764234, 26.390133, -6.7395024, 3.6925054, 26.67168, -6.9665236, 3.8246672, 26.401512, -6.892614, 4.0271807, 26.316252, -7.0833793, 4.2717676, 25.97569, -6.2885137, 4.9992027, 25.899937, -6.5268407, 4.3764234, 26.390133, -6.2885137, 4.9992027, 25.899937, -7.0833793, 4.2717676, 25.97569, -6.9840283, 4.5172915, 25.75856, -6.2885137, 4.9992027, 25.899937, -6.9840283, 4.5172915, 25.75856, -6.7441907, 4.708605, 25.804724, -6.4090276, 5.1228185, 25.524706, -6.100014, 5.3618755, 25.36038, -6.2885137, 4.9992027, 25.899937, -6.100014, 5.3618755, 25.36038, -6.4090276, 5.1228185, 25.524706, -6.6533313, 5.102206, 25.24277, -6.100014, 5.3618755, 25.36038, -6.6533313, 5.102206, 25.24277, -6.5840898, 5.185566, 25.139408, -6.2885137, 4.9992027, 25.899937, -6.049317, 4.232349, 26.135853, -6.5268407, 4.3764234, 26.390133, -6.123463, 4.6506386, 25.980106, -6.049317, 4.232349, 26.135853, -6.2885137, 4.9992027, 25.899937, -6.049317, 4.232349, 26.135853, -6.123463, 4.6506386, 25.980106, -5.8564105, 4.2503223, 25.933277, -6.049317, 4.232349, 26.135853, -5.8564105, 4.2503223, 25.933277, -5.898976, 4.1555033, 26.03533, -6.2885137, 4.9992027, 25.899937, -5.822971, 4.3798666, 25.900269, -6.123463, 4.6506386, 25.980106, -6.2885137, 4.9992027, 25.899937, -5.773127, 4.5062346, 25.794031, -5.822971, 4.3798666, 25.900269, -6.7441907, 4.708605, 25.804724, -6.8905606, 4.769124, 25.571657, -6.2885137, 4.9992027, 25.899937, -6.7441907, 4.708605, 25.804724, -6.933764, 4.659773, 25.663965, -6.8905606, 4.769124, 25.571657, -6.100014, 5.3618755, 25.36038, -5.898379, 4.8849564, 25.608694, -6.2885137, 4.9992027, 25.899937, -5.898379, 4.8849564, 25.608694, -6.100014, 5.3618755, 25.36038, -5.702927, 4.9555445, 25.310673, -5.898379, 4.8849564, 25.608694, -5.702927, 4.9555445, 25.310673, -5.727136, 4.8010197, 25.48896, -6.4090276, 5.1228185, 25.524706, -6.8905606, 4.769124, 25.571657, -6.712791, 4.966251, 25.297207, -6.4090276, 5.1228185, 25.524706, -6.2885137, 4.9992027, 25.899937, -6.8905606, 4.769124, 25.571657, -5.951238, 5.4974275, 24.761002, -5.8909087, 5.2601395, 25.158152, -6.100014, 5.3618755, 25.36038, -5.8909087, 5.2601395, 25.158152, -5.951238, 5.4974275, 24.761002, -5.857908, 5.4214015, 24.738348, -5.8909087, 5.2601395, 25.158152, -5.857908, 5.4214015, 24.738348, -5.786898, 5.237897, 24.993658, -6.892614, 4.0271807, 26.316252, -7.0833793, 4.2717676, 25.97569, -6.5268407, 4.3764234, 26.390133, -6.892614, 4.0271807, 26.316252, -7.084641, 4.049944, 26.135506, -7.0833793, 4.2717676, 25.97569, -6.9665236, 3.8246672, 26.401512, -7.0913386, 3.8691072, 26.228043, -6.892614, 4.0271807, 26.316252, -6.9665236, 3.8246672, 26.401512, -7.0892367, 3.8087032, 26.28731, -7.0913386, 3.8691072, 26.228043, -6.9665236, 3.8246672, 26.401512, -6.7395024, 3.6925054, 26.67168, -7.0888734, 3.6620417, 26.401033, -6.9334645, 3.3768253, 26.686901, -7.0888734, 3.6620417, 26.401033, -6.7395024, 3.6925054, 26.67168, -6.9334645, 3.3768253, 26.686901, -7.0596833, 3.4167135, 26.568975, -7.0888734, 3.6620417, 26.401033, -6.3319364, 5.7359695, 25.081383, -5.9698243, 5.384118, 24.680239, -5.904365, 5.4435186, 24.765022, -6.3319364, 5.7359695, 25.081383, -6.686463, 5.4666615, 24.73655, -5.9698243, 5.384118, 24.680239, -6.155786, 5.38664, 25.487875, -6.125067, 5.5240107, 25.150925, -6.053208, 5.3827543, 25.27274, -6.155786, 5.38664, 25.487875, -6.3319364, 5.7359695, 25.081383, -6.125067, 5.5240107, 25.150925, -8.413198, 4.890188, 26.407503, -7.970281, 5.3087463, 26.08081, -7.7057996, 5.0614824, 26.335453, -8.413198, 4.890188, 26.407503, -8.465763, 4.9176445, 26.35488, -7.970281, 5.3087463, 26.08081, -7.970281, 5.3087463, 26.08081, -7.5340934, 5.103933, 26.298006, -7.7057996, 5.0614824, 26.335453, -7.970281, 5.3087463, 26.08081, -7.592959, 5.3087974, 26.109215, -7.5340934, 5.103933, 26.298006, -7.592959, 5.3087974, 26.109215, -7.970281, 5.3087463, 26.08081, -7.4463277, 5.651639, 25.785309, -7.592959, 5.3087974, 26.109215, -7.4463277, 5.651639, 25.785309, -7.0733275, 5.2124443, 26.196064, -7.592959, 5.3087974, 26.109215, -7.0733275, 5.2124443, 26.196064, -7.379638, 5.141446, 26.26409, -7.0733275, 5.2124443, 26.196064, -7.0267406, 5.3698435, 26.018854, -7.005255, 5.218994, 26.151365, -7.0733275, 5.2124443, 26.196064, -7.4463277, 5.651639, 25.785309, -7.0267406, 5.3698435, 26.018854, -6.5752587, 5.356027, 25.89963, -6.46051, 5.5302234, 25.563005, -6.4677835, 5.358314, 25.792141, -6.5752587, 5.356027, 25.89963, -6.836881, 5.81112, 25.415369, -6.46051, 5.5302234, 25.563005, -8.498926, 4.900632, 26.286041, -7.970281, 5.3087463, 26.08081, -8.465763, 4.9176445, 26.35488, -7.970281, 5.3087463, 26.08081, -8.498926, 4.900632, 26.286041, -8.239595, 5.077715, 26.094341, -8.239595, 5.077715, 26.094341, -8.498926, 4.900632, 26.286041, -8.351461, 4.9742537, 26.046995, -7.970281, 5.3087463, 26.08081, -7.8256736, 5.34012, 25.656843, -7.4463277, 5.651639, 25.785309, -7.970281, 5.3087463, 26.08081, -7.9896913, 5.2398424, 25.73777, -7.8256736, 5.34012, 25.656843, -7.744165, 5.310528, 25.246101, -6.836881, 5.81112, 25.415369, -7.4463277, 5.651639, 25.785309, -6.836881, 5.81112, 25.415369, -7.744165, 5.310528, 25.246101, -7.485513, 5.35617, 25.082954, -6.836881, 5.81112, 25.415369, -7.485513, 5.35617, 25.082954, -7.2575336, 5.5329638, 25.196087, -6.7352633, 5.6665816, 25.089825, -6.3319364, 5.7359695, 25.081383, -6.836881, 5.81112, 25.415369, -6.3319364, 5.7359695, 25.081383, -6.7352633, 5.6665816, 25.089825, -6.815335, 5.4714, 24.787678, -6.3319364, 5.7359695, 25.081383, -6.815335, 5.4714, 24.787678, -6.686463, 5.4666615, 24.73655, -6.836881, 5.81112, 25.415369, -7.0267406, 5.3698435, 26.018854, -7.4463277, 5.651639, 25.785309, -6.8723903, 5.5936527, 25.718147, -7.0267406, 5.3698435, 26.018854, -6.836881, 5.81112, 25.415369, -7.0267406, 5.3698435, 26.018854, -6.8723903, 5.5936527, 25.718147, -6.7732606, 5.244132, 25.999937, -7.0267406, 5.3698435, 26.018854, -6.7732606, 5.244132, 25.999937, -6.888104, 5.2399597, 26.077604, -6.836881, 5.81112, 25.415369, -6.703796, 5.3285036, 25.979181, -6.8723903, 5.5936527, 25.718147, -6.836881, 5.81112, 25.415369, -6.5752587, 5.356027, 25.89963, -6.703796, 5.3285036, 25.979181, -7.2575336, 5.5329638, 25.196087, -7.249745, 5.4294252, 24.944311, -6.836881, 5.81112, 25.415369, -7.2575336, 5.5329638, 25.196087, -7.3613477, 5.405882, 25.013523, -7.249745, 5.4294252, 24.944311, -6.3319364, 5.7359695, 25.081383, -6.46051, 5.5302234, 25.563005, -6.836881, 5.81112, 25.415369, -6.46051, 5.5302234, 25.563005, -6.3319364, 5.7359695, 25.081383, -6.155786, 5.38664, 25.487875, -6.46051, 5.5302234, 25.563005, -6.155786, 5.38664, 25.487875, -6.3074155, 5.384823, 25.640076, -6.7352633, 5.6665816, 25.089825, -7.249745, 5.4294252, 24.944311, -6.916063, 5.3964624, 24.806072, -6.7352633, 5.6665816, 25.089825, -6.836881, 5.81112, 25.415369, -7.249745, 5.4294252, 24.944311, -5.904365, 5.4435186, 24.765022, -6.125067, 5.5240107, 25.150925, -6.3319364, 5.7359695, 25.081383, -6.125067, 5.5240107, 25.150925, -5.904365, 5.4435186, 24.765022, -5.8518662, 5.369458, 24.847279, -6.125067, 5.5240107, 25.150925, -5.8518662, 5.369458, 24.847279, -5.982039, 5.3976545, 25.132534, -7.8256736, 5.34012, 25.656843, -7.744165, 5.310528, 25.246101, -7.4463277, 5.651639, 25.785309, -7.8256736, 5.34012, 25.656843, -7.8908563, 5.243067, 25.42438, -7.744165, 5.310528, 25.246101, -7.9896913, 5.2398424, 25.73777, -7.989692, 5.1600666, 25.528631, -7.8256736, 5.34012, 25.656843, -7.9896913, 5.2398424, 25.73777, -8.038292, 5.1534004, 25.594398, -7.989692, 5.1600666, 25.528631, -7.9896913, 5.2398424, 25.73777, -7.970281, 5.3087463, 26.08081, -8.13969, 5.114773, 25.721031, -8.239595, 5.077715, 26.094341, -8.13969, 5.114773, 25.721031, -7.970281, 5.3087463, 26.08081, -8.239595, 5.077715, 26.094341, -8.274294, 5.0338635, 25.932472, -8.13969, 5.114773, 25.721031, -6.331285, 5.6798854, 24.653553, -5.876188, 5.388583, 24.768026, -5.910171, 5.4440794, 24.847443, -6.331285, 5.6798854, 24.653553, -6.2332807, 5.427716, 24.268461, -5.876188, 5.388583, 24.768026, -6.555012, 5.4120646, 24.973154, -6.2882495, 5.5129185, 24.845657, -6.350158, 5.405257, 24.957108, -6.555012, 5.4120646, 24.973154, -6.331285, 5.6798854, 24.653553, -6.2882495, 5.5129185, 24.845657, -8.316685, 4.926779, 23.737947, -7.8548923, 5.297988, 23.903517, -7.925697, 5.1127415, 24.21713, -8.316685, 4.926779, 23.737947, -8.301708, 4.9451656, 23.675941, -7.8548923, 5.297988, 23.903517, -7.8548923, 5.297988, 23.903517, -7.815824, 5.15586, 24.324652, -7.925697, 5.1127415, 24.21713, -7.8548923, 5.297988, 23.903517, -7.6972337, 5.3176384, 24.191387, -7.815824, 5.15586, 24.324652, -7.6972337, 5.3176384, 24.191387, -7.8548923, 5.297988, 23.903517, -7.37958, 5.598157, 24.143217, -7.6972337, 5.3176384, 24.191387, -7.37958, 5.598157, 24.143217, -7.52163, 5.2640796, 24.611933, -7.6972337, 5.3176384, 24.191387, -7.52163, 5.2640796, 24.611933, -7.717137, 5.1935024, 24.421171, -7.52163, 5.2640796, 24.611933, -7.3604617, 5.389035, 24.563318, -7.457949, 5.2713842, 24.642237, -7.52163, 5.2640796, 24.611933, -7.37958, 5.598157, 24.143217, -7.3604617, 5.389035, 24.563318, -7.058254, 5.3891068, 24.842972, -6.74797, 5.522382, 24.775219, -6.928924, 5.3911495, 24.875774, -7.058254, 5.3891068, 24.842972, -6.8127713, 5.7382603, 24.422806, -6.74797, 5.522382, 24.775219, -8.2651825, 4.9275074, 23.624132, -7.8548923, 5.297988, 23.903517, -8.301708, 4.9451656, 23.675941, -7.8548923, 5.297988, 23.903517, -8.2651825, 4.9275074, 23.624132, -7.9951816, 5.089792, 23.725204, -7.9951816, 5.089792, 23.725204, -8.2651825, 4.9275074, 23.624132, -8.016663, 4.994527, 23.62873, -7.8548923, 5.297988, 23.903517, -7.4628644, 5.3177576, 23.831608, -7.37958, 5.598157, 24.143217, -7.8548923, 5.297988, 23.903517, -7.6030936, 5.2291493, 23.751238, -7.4628644, 5.3177576, 23.831608, -7.1153607, 5.282588, 23.72141, -6.8127713, 5.7382603, 24.422806, -7.37958, 5.598157, 24.143217, -6.8127713, 5.7382603, 24.422806, -7.1153607, 5.282588, 23.72141, -6.87394, 5.326008, 23.83848, -6.8127713, 5.7382603, 24.422806, -6.87394, 5.326008, 23.83848, -6.8445373, 5.4831157, 24.042107, -6.518, 5.605947, 24.369406, -6.331285, 5.6798854, 24.653553, -6.8127713, 5.7382603, 24.422806, -6.331285, 5.6798854, 24.653553, -6.518, 5.605947, 24.369406, -6.3297257, 5.428781, 24.196327, -6.331285, 5.6798854, 24.653553, -6.3297257, 5.428781, 24.196327, -6.2332807, 5.427716, 24.268461, -6.8127713, 5.7382603, 24.422806, -7.3604617, 5.389035, 24.563318, -7.37958, 5.598157, 24.143217, -7.056313, 5.567637, 24.537035, -7.3604617, 5.389035, 24.563318, -6.8127713, 5.7382603, 24.422806, -7.3604617, 5.389035, 24.563318, -7.056313, 5.567637, 24.537035, -7.24099, 5.2982535, 24.745642, -7.3604617, 5.389035, 24.563318, -7.24099, 5.2982535, 24.745642, -7.3485713, 5.2914233, 24.69535, -6.8127713, 5.7382603, 24.422806, -7.1779733, 5.3646665, 24.78445, -7.056313, 5.567637, 24.537035, -6.8127713, 5.7382603, 24.422806, -7.058254, 5.3891068, 24.842972, -7.1779733, 5.3646665, 24.78445, -6.8445373, 5.4831157, 24.042107, -6.6547856, 5.388571, 23.94828, -6.8127713, 5.7382603, 24.422806, -6.8445373, 5.4831157, 24.042107, -6.7587676, 5.367548, 23.897482, -6.6547856, 5.388571, 23.94828, -6.331285, 5.6798854, 24.653553, -6.74797, 5.522382, 24.775219, -6.8127713, 5.7382603, 24.422806, -6.74797, 5.522382, 24.775219, -6.331285, 5.6798854, 24.653553, -6.555012, 5.4120646, 24.973154, -6.74797, 5.522382, 24.775219, -6.555012, 5.4120646, 24.973154, -6.737456, 5.410923, 24.927305, -6.518, 5.605947, 24.369406, -6.6547856, 5.388571, 23.94828, -6.404985, 5.3717523, 24.133698, -6.518, 5.605947, 24.369406, -6.8127713, 5.7382603, 24.422806, -6.6547856, 5.388571, 23.94828, -5.910171, 5.4440794, 24.847443, -6.2882495, 5.5129185, 24.845657, -6.331285, 5.6798854, 24.653553, -6.2882495, 5.5129185, 24.845657, -5.910171, 5.4440794, 24.847443, -5.947083, 5.38731, 24.924572, -6.2882495, 5.5129185, 24.845657, -5.947083, 5.38731, 24.924572, -6.211325, 5.4130735, 24.94913, -7.4628644, 5.3177576, 23.831608, -7.1153607, 5.282588, 23.72141, -7.37958, 5.598157, 24.143217, -7.4628644, 5.3177576, 23.831608, -7.319452, 5.226307, 23.692755, -7.1153607, 5.282588, 23.72141, -7.6030936, 5.2291493, 23.751238, -7.4537644, 5.1571455, 23.66732, -7.4628644, 5.3177576, 23.831608, -7.6030936, 5.2291493, 23.751238, -7.522962, 5.150751, 23.66025, -7.4537644, 5.1571455, 23.66732, -7.6030936, 5.2291493, 23.751238, -7.8548923, 5.297988, 23.903517, -7.6649528, 5.116803, 23.641397, -7.9951816, 5.089792, 23.725204, -7.6649528, 5.116803, 23.641397, -7.8548923, 5.297988, 23.903517, -7.9951816, 5.089792, 23.725204, -7.8895354, 5.0471697, 23.63519, -7.6649528, 5.116803, 23.641397, -5.857347, 5.7116084, 24.363003, -5.903773, 5.378351, 24.927797, -6.00098, 5.4485216, 24.902779, -5.857347, 5.7116084, 24.363003, -5.4018383, 5.389697, 24.404541, -5.903773, 5.378351, 24.927797, -6.2981954, 5.4044847, 24.163559, -6.076615, 5.5257673, 24.45499, -6.2318177, 5.399699, 24.403456, -6.2981954, 5.4044847, 24.163559, -5.857347, 5.7116084, 24.363003, -6.076615, 5.5257673, 24.45499, -5.4074144, 4.736121, 21.818316, -5.439855, 5.177844, 22.396505, -5.831164, 4.97289, 22.37704, -5.4074144, 4.736121, 21.818316, -5.3315473, 4.755287, 21.823586, -5.439855, 5.177844, 22.396505, -5.439855, 5.177844, 22.396505, -5.9201818, 5.030147, 22.5292, -5.831164, 4.97289, 22.37704, -5.439855, 5.177844, 22.396505, -5.7215886, 5.217056, 22.642551, -5.9201818, 5.030147, 22.5292, -5.7215886, 5.217056, 22.642551, -5.439855, 5.177844, 22.396505, -5.5580716, 5.554124, 23.009472, -5.7215886, 5.217056, 22.642551, -5.5580716, 5.554124, 23.009472, -6.160071, 5.178384, 22.936909, -5.7215886, 5.217056, 22.642551, -6.160071, 5.178384, 22.936909, -6.000282, 5.0809345, 22.665983, -6.160071, 5.178384, 22.936909, -6.046327, 5.3291693, 23.119432, -6.1793313, 5.1894274, 23.018368, -6.160071, 5.178384, 22.936909, -5.5580716, 5.554124, 23.009472, -6.046327, 5.3291693, 23.119432, -6.2826037, 5.3541317, 23.53723, -6.1099467, 5.52, 23.893438, -6.287435, 5.3614902, 23.69768, -6.2826037, 5.3541317, 23.53723, -5.7123218, 5.753208, 23.741987, -6.1099467, 5.52, 23.893438, -5.2635365, 4.7313147, 21.856642, -5.439855, 5.177844, 22.396505, -5.3315473, 4.755287, 21.823586, -5.439855, 5.177844, 22.396505, -5.2635365, 4.7313147, 21.856642, -5.291579, 4.922532, 22.195475, -5.291579, 4.922532, 22.195475, -5.2635365, 4.7313147, 21.856642, -5.19895, 4.8064184, 22.150917, -5.439855, 5.177844, 22.396505, -5.24503, 5.200137, 22.848965, -5.5580716, 5.554124, 23.009472, -5.439855, 5.177844, 22.396505, -5.1995373, 5.0887403, 22.666178, -5.24503, 5.200137, 22.848965, -5.0281267, 5.1566877, 23.240654, -5.7123218, 5.753208, 23.741987, -5.5580716, 5.554124, 23.009472, -5.7123218, 5.753208, 23.741987, -5.0281267, 5.1566877, 23.240654, -5.097282, 5.219322, 23.551094, -5.7123218, 5.753208, 23.741987, -5.097282, 5.219322, 23.551094, -5.3022, 5.4230766, 23.628416, -5.577439, 5.6020293, 24.084873, -5.857347, 5.7116084, 24.363003, -5.7123218, 5.753208, 23.741987, -5.857347, 5.7116084, 24.363003, -5.577439, 5.6020293, 24.084873, -5.342383, 5.384054, 24.275276, -5.857347, 5.7116084, 24.363003, -5.342383, 5.384054, 24.275276, -5.4018383, 5.389697, 24.404541, -5.7123218, 5.753208, 23.741987, -6.046327, 5.3291693, 23.119432, -5.5580716, 5.554124, 23.009472, -5.917007, 5.5500546, 23.476519, -6.046327, 5.3291693, 23.119432, -5.7123218, 5.753208, 23.741987, -6.046327, 5.3291693, 23.119432, -5.917007, 5.5500546, 23.476519, -6.244391, 5.2299757, 23.296265, -6.046327, 5.3291693, 23.119432, -6.244391, 5.2299757, 23.296265, -6.210569, 5.21813, 23.159012, -5.7123218, 5.753208, 23.741987, -6.2491727, 5.318202, 23.382605, -5.917007, 5.5500546, 23.476519, -5.7123218, 5.753208, 23.741987, -6.2826037, 5.3541317, 23.53723, -6.2491727, 5.318202, 23.382605, -5.3022, 5.4230766, 23.628416, -5.154279, 5.307758, 23.83554, -5.7123218, 5.753208, 23.741987, -5.3022, 5.4230766, 23.628416, -5.1251884, 5.2768984, 23.701454, -5.154279, 5.307758, 23.83554, -5.857347, 5.7116084, 24.363003, -6.1099467, 5.52, 23.893438, -5.7123218, 5.753208, 23.741987, -6.1099467, 5.52, 23.893438, -5.857347, 5.7116084, 24.363003, -6.2981954, 5.4044847, 24.163559, -6.1099467, 5.52, 23.893438, -6.2981954, 5.4044847, 24.163559, -6.291105, 5.3954716, 23.937151, -5.577439, 5.6020293, 24.084873, -5.154279, 5.307758, 23.83554, -5.3127255, 5.301915, 24.168657, -5.577439, 5.6020293, 24.084873, -5.7123218, 5.753208, 23.741987, -5.154279, 5.307758, 23.83554, -6.00098, 5.4485216, 24.902779, -6.076615, 5.5257673, 24.45499, -5.857347, 5.7116084, 24.363003, -6.076615, 5.5257673, 24.45499, -6.00098, 5.4485216, 24.902779, -6.1017814, 5.3846993, 24.875137, -6.076615, 5.5257673, 24.45499, -6.1017814, 5.3846993, 24.875137, -6.184852, 5.4140406, 24.567734, -5.24503, 5.200137, 22.848965, -5.0281267, 5.1566877, 23.240654, -5.5580716, 5.554124, 23.009472, -5.24503, 5.200137, 22.848965, -5.0552382, 5.0847697, 22.992046, -5.0281267, 5.1566877, 23.240654, -5.1995373, 5.0887403, 22.666178, -5.0783086, 4.9989347, 22.825645, -5.24503, 5.200137, 22.848965, -5.1995373, 5.0887403, 22.666178, -5.0852733, 4.9907217, 22.742779, -5.0783086, 4.9989347, 22.825645, -5.1995373, 5.0887403, 22.666178, -5.439855, 5.177844, 22.396505, -5.102995, 4.9489727, 22.570704, -5.291579, 4.922532, 22.195475, -5.102995, 4.9489727, 22.570704, -5.439855, 5.177844, 22.396505, -5.291579, 4.922532, 22.195475, -5.163247, 4.866782, 22.303133, -5.102995, 4.9489727, 22.570704, -5.488422, 5.4559135, 25.067163, -6.115338, 5.4586115, 24.851, -6.0311418, 5.4854546, 24.766218, -5.488422, 5.4559135, 25.067163, -5.8165927, 5.2966948, 25.486755, -6.115338, 5.4586115, 24.851, -5.37565, 5.0100093, 24.717901, -5.60895, 5.3052464, 24.837267, -5.5913396, 5.1425714, 24.705996, -5.37565, 5.0100093, 24.717901, -5.488422, 5.4559135, 25.067163, -5.60895, 5.3052464, 24.837267, -3.997026, 3.4084823, 26.23418, -4.2373033, 4.0717287, 26.033955, -4.2301164, 3.8139224, 25.675167, -3.997026, 3.4084823, 26.23418, -4.010387, 3.441938, 26.303183, -4.2373033, 4.0717287, 26.033955, -4.2373033, 4.0717287, 26.033955, -4.3043237, 3.923424, 25.547739, -4.2301164, 3.8139224, 25.675167, -4.2373033, 4.0717287, 26.033955, -4.351472, 4.177483, 25.700325, -4.3043237, 3.923424, 25.547739, -4.351472, 4.177483, 25.700325, -4.2373033, 4.0717287, 26.033955, -4.5209985, 4.6817417, 25.74504, -4.351472, 4.177483, 25.700325, -4.5209985, 4.6817417, 25.74504, -4.504621, 4.2121515, 25.204073, -4.351472, 4.177483, 25.700325, -4.504621, 4.2121515, 25.204073, -4.3712316, 4.021261, 25.432993, -4.504621, 4.2121515, 25.204073, -4.6092005, 4.455448, 25.259403, -4.5602574, 4.259628, 25.160809, -4.504621, 4.2121515, 25.204073, -4.5209985, 4.6817417, 25.74504, -4.6092005, 4.455448, 25.259403, -4.8856854, 4.646908, 24.917786, -5.1403203, 5.003999, 24.975368, -5.0130434, 4.735046, 24.86499, -4.8856854, 4.646908, 24.917786, -4.988566, 5.1979246, 25.387373, -5.1403203, 5.003999, 24.975368, -4.0683527, 3.4522138, 26.357347, -4.2373033, 4.0717287, 26.033955, -4.010387, 3.441938, 26.303183, -4.2373033, 4.0717287, 26.033955, -4.0683527, 3.4522138, 26.357347, -4.242745, 3.782452, 26.231682, -4.242745, 3.782452, 26.231682, -4.0683527, 3.4522138, 26.357347, -4.2885528, 3.6797438, 26.329477, -4.2373033, 4.0717287, 26.033955, -4.656346, 4.3628387, 26.08645, -4.5209985, 4.6817417, 25.74504, -4.2373033, 4.0717287, 26.033955, -4.57377, 4.1835814, 26.181997, -4.656346, 4.3628387, 26.08645, -5.062138, 4.5712886, 26.17474, -4.988566, 5.1979246, 25.387373, -4.5209985, 4.6817417, 25.74504, -4.988566, 5.1979246, 25.387373, -5.062138, 4.5712886, 26.17474, -5.2679048, 4.7707634, 26.016972, -4.988566, 5.1979246, 25.387373, -5.2679048, 4.7707634, 26.016972, -5.1766024, 4.9423556, 25.806808, -5.3932915, 5.275302, 25.41678, -5.488422, 5.4559135, 25.067163, -4.988566, 5.1979246, 25.387373, -5.488422, 5.4559135, 25.067163, -5.3932915, 5.275302, 25.41678, -5.7287784, 5.236825, 25.581738, -5.488422, 5.4559135, 25.067163, -5.7287784, 5.236825, 25.581738, -5.8165927, 5.2966948, 25.486755, -4.988566, 5.1979246, 25.387373, -4.6092005, 4.455448, 25.259403, -4.5209985, 4.6817417, 25.74504, -4.825552, 4.8508716, 25.277138, -4.6092005, 4.455448, 25.259403, -4.988566, 5.1979246, 25.387373, -4.6092005, 4.455448, 25.259403, -4.825552, 4.8508716, 25.277138, -4.749141, 4.4242043, 25.014292, -4.6092005, 4.455448, 25.259403, -4.749141, 4.4242043, 25.014292, -4.653323, 4.3502817, 25.089447, -4.988566, 5.1979246, 25.387373, -4.784744, 4.543359, 24.994701, -4.825552, 4.8508716, 25.277138, -4.988566, 5.1979246, 25.387373, -4.8856854, 4.646908, 24.917786, -4.784744, 4.543359, 24.994701, -5.1766024, 4.9423556, 25.806808, -5.4469156, 4.981415, 25.883078, -4.988566, 5.1979246, 25.387373, -5.1766024, 4.9423556, 25.806808, -5.3591576, 4.891834, 25.949957, -5.4469156, 4.981415, 25.883078, -5.488422, 5.4559135, 25.067163, -5.1403203, 5.003999, 24.975368, -4.988566, 5.1979246, 25.387373, -5.1403203, 5.003999, 24.975368, -5.488422, 5.4559135, 25.067163, -5.37565, 5.0100093, 24.717901, -5.1403203, 5.003999, 24.975368, -5.37565, 5.0100093, 24.717901, -5.1955976, 4.8869286, 24.792862, -5.3932915, 5.275302, 25.41678, -5.4469156, 4.981415, 25.883078, -5.6794295, 5.1171756, 25.630447, -5.3932915, 5.275302, 25.41678, -4.988566, 5.1979246, 25.387373, -5.4469156, 4.981415, 25.883078, -6.0311418, 5.4854546, 24.766218, -5.60895, 5.3052464, 24.837267, -5.488422, 5.4559135, 25.067163, -5.60895, 5.3052464, 24.837267, -6.0311418, 5.4854546, 24.766218, -6.01732, 5.3982096, 24.681461, -5.60895, 5.3052464, 24.837267, -6.01732, 5.3982096, 24.681461, -5.733069, 5.248727, 24.701923, -4.656346, 4.3628387, 26.08645, -5.062138, 4.5712886, 26.17474, -4.5209985, 4.6817417, 25.74504, -4.656346, 4.3628387, 26.08645, -4.8856683, 4.377059, 26.22222, -5.062138, 4.5712886, 26.17474, -4.57377, 4.1835814, 26.181997, -4.779701, 4.213683, 26.243425, -4.656346, 4.3628387, 26.08645, -4.57377, 4.1835814, 26.181997, -4.715779, 4.162856, 26.263649, -4.779701, 4.213683, 26.243425, -4.57377, 4.1835814, 26.181997, -4.2373033, 4.0717287, 26.033955, -4.590958, 4.0354905, 26.29871, -4.242745, 3.782452, 26.231682, -4.590958, 4.0354905, 26.29871, -4.2373033, 4.0717287, 26.033955, -4.242745, 3.782452, 26.231682, -4.395331, 3.8163853, 26.320553, -4.590958, 4.0354905, 26.29871, -6.1042504, 1.2728562, 23.197392, -5.918094, 2.7018955, 23.690025, -5.9148564, 2.7549148, 23.524578, -6.1042504, 1.2728562, 23.197392, -6.1098676, 1.2173094, 23.399563, -5.918094, 2.7018955, 23.690025, -6.4102006, -0.38891935, 23.115858, -6.1098676, 1.2173094, 23.399563, -6.1042504, 1.2728562, 23.197392, -6.4102006, -0.38891935, 23.115858, -6.407615, -0.42018914, 23.34877, -6.1098676, 1.2173094, 23.399563, -6.445969, 1.214475, 23.560837, -6.334398, 2.7400944, 23.713541, -6.206943, 2.6917243, 23.821266, -6.445969, 1.214475, 23.560837, -6.593639, 1.2685848, 23.43337, -6.334398, 2.7400944, 23.713541, -6.45285, 1.3504215, 23.077362, -6.04308, 2.8031602, 23.417374, -6.2159896, 2.8219187, 23.420893, -6.45285, 1.3504215, 23.077362, -6.2506223, 1.3273255, 23.06642, -6.04308, 2.8031602, 23.417374, -6.206943, 2.6917243, 23.821266, -6.1571717, 4.087394, 24.238876, -6.071984, 4.053143, 24.309086, -6.206943, 2.6917243, 23.821266, -6.334398, 2.7400944, 23.713541, -6.1571717, 4.087394, 24.238876, -5.9148564, 2.7549148, 23.524578, -5.878671, 4.066166, 24.22505, -5.875483, 4.1052194, 24.1148, -5.9148564, 2.7549148, 23.524578, -5.918094, 2.7018955, 23.690025, -5.878671, 4.066166, 24.22505, -5.875483, 4.1052194, 24.1148, -5.878671, 4.066166, 24.22505, -5.8760715, 4.7822523, 24.560642, -5.875483, 4.1052194, 24.1148, -5.8760715, 4.7822523, 24.560642, -5.8734727, 4.8202696, 24.482874, -6.1098676, 1.2173094, 23.399563, -6.0342355, 2.6733067, 23.816893, -5.918094, 2.7018955, 23.690025, -6.1098676, 1.2173094, 23.399563, -6.2450395, 1.1910194, 23.5534, -6.0342355, 2.6733067, 23.816893, -5.918094, 2.7018955, 23.690025, -6.0342355, 2.6733067, 23.816893, -5.9568696, 4.0428257, 24.307844, -5.918094, 2.7018955, 23.690025, -5.9568696, 4.0428257, 24.307844, -5.878671, 4.066166, 24.22505, -6.593639, 1.2685848, 23.43337, -6.3314233, 2.7938638, 23.547709, -6.334398, 2.7400944, 23.713541, -6.593639, 1.2685848, 23.43337, -6.589192, 1.3239617, 23.234522, -6.3314233, 2.7938638, 23.547709, -6.334398, 2.7400944, 23.713541, -6.3314233, 2.7938638, 23.547709, -6.1551642, 4.1276875, 24.129848, -6.334398, 2.7400944, 23.713541, -6.1551642, 4.1276875, 24.129848, -6.1571717, 4.087394, 24.238876, -6.2159896, 2.8219187, 23.420893, -5.9612355, 4.1389728, 24.042906, -6.0775657, 4.150184, 24.04522, -6.2159896, 2.8219187, 23.420893, -6.04308, 2.8031602, 23.417374, -5.9612355, 4.1389728, 24.042906, -6.2506223, 1.3273255, 23.06642, -5.9148564, 2.7549148, 23.524578, -6.04308, 2.8031602, 23.417374, -6.2506223, 1.3273255, 23.06642, -6.1042504, 1.2728562, 23.197392, -5.9148564, 2.7549148, 23.524578, -6.04308, 2.8031602, 23.417374, -5.875483, 4.1052194, 24.1148, -5.9612355, 4.1389728, 24.042906, -6.04308, 2.8031602, 23.417374, -5.9148564, 2.7549148, 23.524578, -5.875483, 4.1052194, 24.1148, -6.589192, 1.3239617, 23.234522, -6.2159896, 2.8219187, 23.420893, -6.3314233, 2.7938638, 23.547709, -6.589192, 1.3239617, 23.234522, -6.45285, 1.3504215, 23.077362, -6.2159896, 2.8219187, 23.420893, -6.971047, -0.29482222, 23.15477, -6.45285, 1.3504215, 23.077362, -6.589192, 1.3239617, 23.234522, -6.971047, -0.29482222, 23.15477, -6.8199706, -0.29774308, 22.974768, -6.45285, 1.3504215, 23.077362, -6.3314233, 2.7938638, 23.547709, -6.0775657, 4.150184, 24.04522, -6.1551642, 4.1276875, 24.129848, -6.3314233, 2.7938638, 23.547709, -6.2159896, 2.8219187, 23.420893, -6.0775657, 4.150184, 24.04522, -6.1551642, 4.1276875, 24.129848, -6.0775657, 4.150184, 24.04522, -6.023566, 4.8536663, 24.431192, -6.1551642, 4.1276875, 24.129848, -6.023566, 4.8536663, 24.431192, -6.082048, 4.828084, 24.48972, -6.020826, 4.7619505, 24.618555, -5.936191, 5.2892733, 24.899128, -5.934734, 4.757289, 24.618954, -6.020826, 4.7619505, 24.618555, -5.999312, 5.2915688, 24.898323, -5.936191, 5.2892733, 24.899128, -5.8760715, 4.7822523, 24.560642, -5.8913436, 5.3461533, 24.805635, -5.8734727, 4.8202696, 24.482874, -5.8760715, 4.7822523, 24.560642, -5.8932214, 5.3139095, 24.859932, -5.8913436, 5.3461533, 24.805635, -5.934734, 4.757289, 24.618954, -5.8932214, 5.3139095, 24.859932, -5.8760715, 4.7822523, 24.560642, -5.934734, 4.757289, 24.618954, -5.936191, 5.2892733, 24.899128, -5.8932214, 5.3139095, 24.859932, -5.9370246, 4.8486996, 24.431229, -6.000772, 5.3705015, 24.767956, -6.023566, 4.8536663, 24.431192, -5.9370246, 4.8486996, 24.431229, -5.937638, 5.3697376, 24.769716, -6.000772, 5.3705015, 24.767956, -5.8734727, 4.8202696, 24.482874, -5.937638, 5.3697376, 24.769716, -5.9370246, 4.8486996, 24.431229, -5.8734727, 4.8202696, 24.482874, -5.8913436, 5.3461533, 24.805635, -5.937638, 5.3697376, 24.769716, -6.082048, 4.828084, 24.48972, -6.0456195, 5.3136215, 24.86145, -6.0841565, 4.790536, 24.567505, -6.082048, 4.828084, 24.48972, -6.0437293, 5.3473988, 24.80811, -6.0456195, 5.3136215, 24.86145, -6.023566, 4.8536663, 24.431192, -6.0437293, 5.3473988, 24.80811, -6.082048, 4.828084, 24.48972, -6.023566, 4.8536663, 24.431192, -6.000772, 5.3705015, 24.767956, -6.0437293, 5.3473988, 24.80811, -6.5591636, -0.41737175, 23.530104, -6.445969, 1.214475, 23.560837, -6.2450395, 1.1910194, 23.5534, -6.5591636, -0.41737175, 23.530104, -6.791864, -0.37708354, 23.536917, -6.445969, 1.214475, 23.560837, -6.0841565, 4.790536, 24.567505, -5.999312, 5.2915688, 24.898323, -6.020826, 4.7619505, 24.618555, -6.0841565, 4.790536, 24.567505, -6.0456195, 5.3136215, 24.86145, -5.999312, 5.2915688, 24.898323, -6.2450395, 1.1910194, 23.5534, -6.206943, 2.6917243, 23.821266, -6.0342355, 2.6733067, 23.816893, -6.2450395, 1.1910194, 23.5534, -6.445969, 1.214475, 23.560837, -6.206943, 2.6917243, 23.821266, -6.791864, -0.37708354, 23.536917, -6.593639, 1.2685848, 23.43337, -6.445969, 1.214475, 23.560837, -6.791864, -0.37708354, 23.536917, -6.9689336, -0.32619548, 23.389013, -6.593639, 1.2685848, 23.43337, -6.8199706, -0.29774308, 22.974768, -6.2506223, 1.3273255, 23.06642, -6.45285, 1.3504215, 23.077362, -6.8199706, -0.29774308, 22.974768, -6.5876184, -0.33810782, 22.968916, -6.2506223, 1.3273255, 23.06642, -6.407615, -0.42018914, 23.34877, -6.2450395, 1.1910194, 23.5534, -6.1098676, 1.2173094, 23.399563, -6.407615, -0.42018914, 23.34877, -6.5591636, -0.41737175, 23.530104, -6.2450395, 1.1910194, 23.5534, -6.9689336, -0.32619548, 23.389013, -6.589192, 1.3239617, 23.234522, -6.593639, 1.2685848, 23.43337, -6.9689336, -0.32619548, 23.389013, -6.971047, -0.29482222, 23.15477, -6.589192, 1.3239617, 23.234522, -6.5876184, -0.33810782, 22.968916, -6.1042504, 1.2728562, 23.197392, -6.2506223, 1.3273255, 23.06642, -6.5876184, -0.33810782, 22.968916, -6.4102006, -0.38891935, 23.115858, -6.1042504, 1.2728562, 23.197392, -6.0342355, 2.6733067, 23.816893, -6.071984, 4.053143, 24.309086, -5.9568696, 4.0428257, 24.307844, -6.0342355, 2.6733067, 23.816893, -6.206943, 2.6917243, 23.821266, -6.071984, 4.053143, 24.309086, -5.9568696, 4.0428257, 24.307844, -6.020826, 4.7619505, 24.618555, -5.934734, 4.757289, 24.618954, -5.9568696, 4.0428257, 24.307844, -6.071984, 4.053143, 24.309086, -6.020826, 4.7619505, 24.618555, -6.071984, 4.053143, 24.309086, -6.0841565, 4.790536, 24.567505, -6.020826, 4.7619505, 24.618555, -6.071984, 4.053143, 24.309086, -6.1571717, 4.087394, 24.238876, -6.0841565, 4.790536, 24.567505, -5.878671, 4.066166, 24.22505, -5.934734, 4.757289, 24.618954, -5.8760715, 4.7822523, 24.560642, -5.878671, 4.066166, 24.22505, -5.9568696, 4.0428257, 24.307844, -5.934734, 4.757289, 24.618954, -6.0775657, 4.150184, 24.04522, -5.9370246, 4.8486996, 24.431229, -6.023566, 4.8536663, 24.431192, -6.0775657, 4.150184, 24.04522, -5.9612355, 4.1389728, 24.042906, -5.9370246, 4.8486996, 24.431229, -5.9612355, 4.1389728, 24.042906, -5.8734727, 4.8202696, 24.482874, -5.9370246, 4.8486996, 24.431229, -5.9612355, 4.1389728, 24.042906, -5.875483, 4.1052194, 24.1148, -5.8734727, 4.8202696, 24.482874, -6.1571717, 4.087394, 24.238876, -6.082048, 4.828084, 24.48972, -6.0841565, 4.790536, 24.567505, -6.1571717, 4.087394, 24.238876, -6.1551642, 4.1276875, 24.129848, -6.082048, 4.828084, 24.48972, -7.3456454, 4.1475077, 21.98113, -7.7547154, 4.0165606, 21.935587, -7.73344, 4.0729046, 21.883982, -7.3456454, 4.1475077, 21.98113, -7.39551, 3.8681917, 22.222395, -7.7547154, 4.0165606, 21.935587, -7.2885756, 4.023938, 21.648745, -7.4439178, 4.086134, 21.816298, -7.4376407, 4.0358357, 21.702383, -7.2885756, 4.023938, 21.648745, -7.3456454, 4.1475077, 21.98113, -7.4439178, 4.086134, 21.816298, -5.835222, 3.136388, 22.094479, -6.143434, 3.5016217, 22.14021, -6.182443, 3.446207, 21.86201, -5.835222, 3.136388, 22.094479, -5.831487, 3.134635, 22.146042, -6.143434, 3.5016217, 22.14021, -6.143434, 3.5016217, 22.14021, -6.276529, 3.5192416, 21.814625, -6.182443, 3.446207, 21.86201, -6.143434, 3.5016217, 22.14021, -6.311259, 3.6133804, 21.96652, -6.276529, 3.5192416, 21.814625, -6.311259, 3.6133804, 21.96652, -6.143434, 3.5016217, 22.14021, -6.488027, 3.838162, 22.121197, -6.311259, 3.6133804, 21.96652, -6.488027, 3.838162, 22.121197, -6.5290217, 3.7089415, 21.687683, -6.311259, 3.6133804, 21.96652, -6.5290217, 3.7089415, 21.687683, -6.3611083, 3.5839481, 21.772032, -6.5290217, 3.7089415, 21.687683, -6.61853, 3.8050375, 21.778206, -6.5801196, 3.7298079, 21.680998, -6.5290217, 3.7089415, 21.687683, -6.488027, 3.838162, 22.121197, -6.61853, 3.8050375, 21.778206, -6.895559, 3.9183722, 21.635805, -7.089803, 4.029269, 21.773304, -6.996865, 3.9430032, 21.63935, -6.895559, 3.9183722, 21.635805, -6.936025, 4.07687, 22.060637, -7.089803, 4.029269, 21.773304, -5.852432, 3.11117, 22.186768, -6.143434, 3.5016217, 22.14021, -5.831487, 3.134635, 22.146042, -6.143434, 3.5016217, 22.14021, -5.852432, 3.11117, 22.186768, -6.0435195, 3.2870784, 22.199028, -6.0435195, 3.2870784, 22.199028, -5.852432, 3.11117, 22.186768, -6.0259957, 3.1908565, 22.246626, -6.143434, 3.5016217, 22.14021, -6.4199605, 3.5414572, 22.270983, -6.488027, 3.838162, 22.121197, -6.143434, 3.5016217, 22.14021, -6.316384, 3.4395418, 22.283651, -6.4199605, 3.5414572, 22.270983, -6.6648736, 3.5251694, 22.411572, -6.936025, 4.07687, 22.060637, -6.488027, 3.838162, 22.121197, -6.936025, 4.07687, 22.060637, -6.6648736, 3.5251694, 22.411572, -6.8574367, 3.616784, 22.385473, -6.936025, 4.07687, 22.060637, -6.8574367, 3.616784, 22.385473, -6.8897033, 3.7860932, 22.274248, -7.1705127, 3.995197, 22.128761, -7.3456454, 4.1475077, 21.98113, -6.936025, 4.07687, 22.060637, -7.3456454, 4.1475077, 21.98113, -7.1705127, 3.995197, 22.128761, -7.311005, 3.8388238, 22.254168, -7.3456454, 4.1475077, 21.98113, -7.311005, 3.8388238, 22.254168, -7.39551, 3.8681917, 22.222395, -6.936025, 4.07687, 22.060637, -6.61853, 3.8050375, 21.778206, -6.488027, 3.838162, 22.121197, -6.808315, 3.9616337, 21.892696, -6.61853, 3.8050375, 21.778206, -6.936025, 4.07687, 22.060637, -6.61853, 3.8050375, 21.778206, -6.808315, 3.9616337, 21.892696, -6.754091, 3.8023548, 21.658047, -6.61853, 3.8050375, 21.778206, -6.754091, 3.8023548, 21.658047, -6.667336, 3.7712486, 21.66921, -6.936025, 4.07687, 22.060637, -6.8002677, 3.871698, 21.648817, -6.808315, 3.9616337, 21.892696, -6.936025, 4.07687, 22.060637, -6.895559, 3.9183722, 21.635805, -6.8002677, 3.871698, 21.648817, -6.8897033, 3.7860932, 22.274248, -7.0301766, 3.7129135, 22.36113, -6.936025, 4.07687, 22.060637, -6.8897033, 3.7860932, 22.274248, -6.94754, 3.6726983, 22.372395, -7.0301766, 3.7129135, 22.36113, -7.3456454, 4.1475077, 21.98113, -7.089803, 4.029269, 21.773304, -6.936025, 4.07687, 22.060637, -7.089803, 4.029269, 21.773304, -7.3456454, 4.1475077, 21.98113, -7.2885756, 4.023938, 21.648745, -7.089803, 4.029269, 21.773304, -7.2885756, 4.023938, 21.648745, -7.145592, 3.9925408, 21.643513, -7.1705127, 3.995197, 22.128761, -7.0301766, 3.7129135, 22.36113, -7.250442, 3.7801287, 22.278788, -7.1705127, 3.995197, 22.128761, -6.936025, 4.07687, 22.060637, -7.0301766, 3.7129135, 22.36113, -7.73344, 4.0729046, 21.883982, -7.4439178, 4.086134, 21.816298, -7.3456454, 4.1475077, 21.98113, -7.4439178, 4.086134, 21.816298, -7.73344, 4.0729046, 21.883982, -7.730845, 4.0477705, 21.8088, -7.4439178, 4.086134, 21.816298, -7.730845, 4.0477705, 21.8088, -7.537543, 4.047288, 21.737965, -6.4199605, 3.5414572, 22.270983, -6.6648736, 3.5251694, 22.411572, -6.488027, 3.838162, 22.121197, -6.4199605, 3.5414572, 22.270983, -6.5177665, 3.4538927, 22.380463, -6.6648736, 3.5251694, 22.411572, -6.316384, 3.4395418, 22.283651, -6.4231963, 3.3844132, 22.362364, -6.4199605, 3.5414572, 22.270983, -6.316384, 3.4395418, 22.283651, -6.372542, 3.369453, 22.350878, -6.4231963, 3.3844132, 22.362364, -6.316384, 3.4395418, 22.283651, -6.143434, 3.5016217, 22.14021, -6.269735, 3.3225741, 22.32892, -6.0435195, 3.2870784, 22.199028, -6.269735, 3.3225741, 22.32892, -6.143434, 3.5016217, 22.14021, -6.0435195, 3.2870784, 22.199028, -6.1135287, 3.244587, 22.275616, -6.269735, 3.3225741, 22.32892, -8.006098, 4.0689516, 22.13492, -7.6670833, 4.0418825, 21.783504, -7.640471, 4.0882125, 21.854998, -8.006098, 4.0689516, 22.13492, -8.1642685, 3.8312206, 21.842627, -7.6670833, 4.0418825, 21.783504, -7.756726, 3.8452916, 22.38111, -7.7926917, 3.9996805, 22.145943, -7.6962695, 3.9147148, 22.219965, -7.756726, 3.8452916, 22.38111, -8.006098, 4.0689516, 22.13492, -7.7926917, 3.9996805, 22.145943, -9.112757, 2.5434918, 23.142544, -8.955492, 3.0269203, 22.925161, -8.686771, 2.9242716, 23.071201, -9.112757, 2.5434918, 23.142544, -9.159386, 2.5504227, 23.109524, -8.955492, 3.0269203, 22.925161, -8.955492, 3.0269203, 22.925161, -8.584085, 3.0207791, 23.038433, -8.686771, 2.9242716, 23.071201, -8.955492, 3.0269203, 22.925161, -8.695301, 3.1616163, 22.923185, -8.584085, 3.0207791, 23.038433, -8.695301, 3.1616163, 22.923185, -8.955492, 3.0269203, 22.925161, -8.715704, 3.4878736, 22.713732, -8.695301, 3.1616163, 22.923185, -8.715704, 3.4878736, 22.713732, -8.307899, 3.2759342, 22.950727, -8.695301, 3.1616163, 22.923185, -8.307899, 3.2759342, 22.950727, -8.491686, 3.1070724, 23.009012, -8.307899, 3.2759342, 22.950727, -8.328896, 3.4248629, 22.82755, -8.266613, 3.3116674, 22.91728, -8.307899, 3.2759342, 22.950727, -8.715704, 3.4878736, 22.713732, -8.328896, 3.4248629, 22.82755, -8.018351, 3.603071, 22.708073, -8.005375, 3.8135586, 22.46657, -7.9505553, 3.661367, 22.623951, -8.018351, 3.603071, 22.708073, -8.361177, 3.876412, 22.41829, -8.005375, 3.8135586, 22.46657, -9.178247, 2.5394235, 23.05604, -8.955492, 3.0269203, 22.925161, -9.159386, 2.5504227, 23.109524, -8.955492, 3.0269203, 22.925161, -9.178247, 2.5394235, 23.05604, -9.064192, 2.7795045, 22.919472, -9.064192, 2.7795045, 22.919472, -9.178247, 2.5394235, 23.05604, -9.110924, 2.6796026, 22.880608, -8.955492, 3.0269203, 22.925161, -8.875946, 3.1743643, 22.591955, -8.715704, 3.4878736, 22.713732, -8.955492, 3.0269203, 22.925161, -8.9538765, 3.0363877, 22.650652, -8.875946, 3.1743643, 22.591955, -8.822695, 3.2558434, 22.270594, -8.361177, 3.876412, 22.41829, -8.715704, 3.4878736, 22.713732, -8.361177, 3.876412, 22.41829, -8.822695, 3.2558434, 22.270594, -8.668564, 3.40597, 22.14398, -8.361177, 3.876412, 22.41829, -8.668564, 3.40597, 22.14398, -8.56176, 3.58513, 22.231379, -8.250622, 3.8801494, 22.13827, -8.006098, 4.0689516, 22.13492, -8.361177, 3.876412, 22.41829, -8.006098, 4.0689516, 22.13492, -8.250622, 3.8801494, 22.13827, -8.250301, 3.7793045, 21.885279, -8.006098, 4.0689516, 22.13492, -8.250301, 3.7793045, 21.885279, -8.1642685, 3.8312206, 21.842627, -8.361177, 3.876412, 22.41829, -8.328896, 3.4248629, 22.82755, -8.715704, 3.4878736, 22.713732, -8.302567, 3.6759555, 22.615335, -8.328896, 3.4248629, 22.82755, -8.361177, 3.876412, 22.41829, -8.328896, 3.4248629, 22.82755, -8.302567, 3.6759555, 22.615335, -8.126291, 3.4355674, 22.803347, -8.328896, 3.4248629, 22.82755, -8.126291, 3.4355674, 22.803347, -8.196812, 3.379901, 22.85993, -8.361177, 3.876412, 22.41829, -8.094337, 3.5251052, 22.770916, -8.302567, 3.6759555, 22.615335, -8.361177, 3.876412, 22.41829, -8.018351, 3.603071, 22.708073, -8.094337, 3.5251052, 22.770916, -8.56176, 3.58513, 22.231379, -8.532204, 3.5644715, 22.029161, -8.361177, 3.876412, 22.41829, -8.56176, 3.58513, 22.231379, -8.598177, 3.497055, 22.083708, -8.532204, 3.5644715, 22.029161, -8.006098, 4.0689516, 22.13492, -8.005375, 3.8135586, 22.46657, -8.361177, 3.876412, 22.41829, -8.005375, 3.8135586, 22.46657, -8.006098, 4.0689516, 22.13492, -7.756726, 3.8452916, 22.38111, -8.005375, 3.8135586, 22.46657, -7.756726, 3.8452916, 22.38111, -7.852491, 3.7639642, 22.499866, -8.250622, 3.8801494, 22.13827, -8.532204, 3.5644715, 22.029161, -8.306704, 3.6845965, 21.918798, -8.250622, 3.8801494, 22.13827, -8.361177, 3.876412, 22.41829, -8.532204, 3.5644715, 22.029161, -7.640471, 4.0882125, 21.854998, -7.7926917, 3.9996805, 22.145943, -8.006098, 4.0689516, 22.13492, -7.7926917, 3.9996805, 22.145943, -7.640471, 4.0882125, 21.854998, -7.576958, 4.04729, 21.902573, -7.7926917, 3.9996805, 22.145943, -7.576958, 4.04729, 21.902573, -7.6565347, 3.9742813, 22.111242, -8.875946, 3.1743643, 22.591955, -8.822695, 3.2558434, 22.270594, -8.715704, 3.4878736, 22.713732, -8.875946, 3.1743643, 22.591955, -8.895857, 3.1287508, 22.405542, -8.822695, 3.2558434, 22.270594, -8.9538765, 3.0363877, 22.650652, -8.940609, 3.017987, 22.493166, -8.875946, 3.1743643, 22.591955, -8.9538765, 3.0363877, 22.650652, -8.9667015, 2.9863772, 22.539291, -8.940609, 3.017987, 22.493166, -8.9538765, 3.0363877, 22.650652, -8.955492, 3.0269203, 22.925161, -9.018259, 2.9038734, 22.63329, -9.064192, 2.7795045, 22.919472, -9.018259, 2.9038734, 22.63329, -8.955492, 3.0269203, 22.925161, -9.064192, 2.7795045, 22.919472, -9.078123, 2.7668862, 22.79162, -9.018259, 2.9038734, 22.63329, -8.007105, 4.2432814, 21.79742, -7.6102357, 3.9935734, 21.830496, -7.6156516, 4.0593348, 21.890636, -8.007105, 4.2432814, 21.79742, -8.03323, 3.9421675, 21.519663, -7.6102357, 3.9935734, 21.830496, -8.098074, 4.0979156, 22.171206, -7.9227233, 4.142306, 21.975924, -7.9388556, 4.0766134, 22.104975, -8.098074, 4.0979156, 22.171206, -8.007105, 4.2432814, 21.79742, -7.9227233, 4.142306, 21.975924, -9.771751, 3.535802, 21.75793, -9.379068, 3.8438268, 21.68758, -9.350674, 3.7584422, 21.997433, -9.771751, 3.535802, 21.75793, -9.776064, 3.537459, 21.700321, -9.379068, 3.8438268, 21.68758, -9.379068, 3.8438268, 21.68758, -9.238958, 3.8097954, 22.04487, -9.350674, 3.7584422, 21.997433, -9.379068, 3.8438268, 21.68758, -9.184797, 3.91052, 21.871664, -9.238958, 3.8097954, 22.04487, -9.184797, 3.91052, 21.871664, -9.379068, 3.8438268, 21.68758, -8.955339, 4.1180353, 21.68752, -9.184797, 3.91052, 21.871664, -8.955339, 4.1180353, 21.68752, -8.938385, 3.9436283, 22.172657, -9.184797, 3.91052, 21.871664, -8.938385, 3.9436283, 22.172657, -9.138344, 3.8554869, 22.087576, -8.938385, 3.9436283, 22.172657, -8.830858, 4.0307946, 22.063833, -8.879677, 3.9519649, 22.17815, -8.938385, 3.9436283, 22.172657, -8.955339, 4.1180353, 21.68752, -8.830858, 4.0307946, 22.063833, -8.525064, 4.0757513, 22.206758, -8.3042145, 4.1594357, 22.040773, -8.414618, 4.077264, 22.197893, -8.525064, 4.0757513, 22.206758, -8.445869, 4.2673163, 21.729216, -8.3042145, 4.1594357, 22.040773, -9.761192, 3.5089111, 21.653206, -9.379068, 3.8438268, 21.68758, -9.776064, 3.537459, 21.700321, -9.379068, 3.8438268, 21.68758, -9.761192, 3.5089111, 21.653206, -9.529911, 3.6422298, 21.627573, -9.529911, 3.6422298, 21.627573, -9.761192, 3.5089111, 21.653206, -9.567165, 3.544581, 21.576538, -9.379068, 3.8438268, 21.68758, -9.097656, 3.8193562, 21.5222, -8.955339, 4.1180353, 21.68752, -9.379068, 3.8438268, 21.68758, -9.2248955, 3.7364876, 21.514492, -9.097656, 3.8193562, 21.5222, -8.856268, 3.7445884, 21.349636, -8.445869, 4.2673163, 21.729216, -8.955339, 4.1180353, 21.68752, -8.445869, 4.2673163, 21.729216, -8.856268, 3.7445884, 21.349636, -8.63674, 3.7917051, 21.369219, -8.445869, 4.2673163, 21.729216, -8.63674, 3.7917051, 21.369219, -8.568174, 3.9699275, 21.489882, -8.232094, 4.130494, 21.637142, -8.007105, 4.2432814, 21.79742, -8.445869, 4.2673163, 21.729216, -8.007105, 4.2432814, 21.79742, -8.232094, 4.130494, 21.637142, -8.128689, 3.9327025, 21.48767, -8.007105, 4.2432814, 21.79742, -8.128689, 3.9327025, 21.48767, -8.03323, 3.9421675, 21.519663, -8.445869, 4.2673163, 21.729216, -8.830858, 4.0307946, 22.063833, -8.955339, 4.1180353, 21.68752, -8.606184, 4.161176, 21.922972, -8.830858, 4.0307946, 22.063833, -8.445869, 4.2673163, 21.729216, -8.830858, 4.0307946, 22.063833, -8.606184, 4.161176, 21.922972, -8.680071, 3.982471, 22.196703, -8.830858, 4.0307946, 22.063833, -8.680071, 3.982471, 22.196703, -8.7803135, 3.9735296, 22.187021, -8.445869, 4.2673163, 21.729216, -8.633348, 4.0487413, 22.19735, -8.606184, 4.161176, 21.922972, -8.445869, 4.2673163, 21.729216, -8.525064, 4.0757513, 22.206758, -8.633348, 4.0487413, 22.19735, -8.568174, 3.9699275, 21.489882, -8.441978, 3.8581774, 21.385015, -8.445869, 4.2673163, 21.729216, -8.568174, 3.9699275, 21.489882, -8.53607, 3.8349931, 21.376839, -8.441978, 3.8581774, 21.385015, -8.007105, 4.2432814, 21.79742, -8.3042145, 4.1594357, 22.040773, -8.445869, 4.2673163, 21.729216, -8.3042145, 4.1594357, 22.040773, -8.007105, 4.2432814, 21.79742, -8.098074, 4.0979156, 22.171206, -8.3042145, 4.1594357, 22.040773, -8.098074, 4.0979156, 22.171206, -8.254183, 4.096847, 22.18368, -8.232094, 4.130494, 21.637142, -8.441978, 3.8581774, 21.385015, -8.191664, 3.867425, 21.469576, -8.232094, 4.130494, 21.637142, -8.445869, 4.2673163, 21.729216, -8.441978, 3.8581774, 21.385015, -7.6156516, 4.0593348, 21.890636, -7.9227233, 4.142306, 21.975924, -8.007105, 4.2432814, 21.79742, -7.9227233, 4.142306, 21.975924, -7.6156516, 4.0593348, 21.890636, -7.62472, 4.0303593, 21.974613, -7.9227233, 4.142306, 21.975924, -7.62472, 4.0303593, 21.974613, -7.833011, 4.0754824, 22.059702, -9.097656, 3.8193562, 21.5222, -8.856268, 3.7445884, 21.349636, -8.955339, 4.1180353, 21.68752, -9.097656, 3.8193562, 21.5222, -9.020979, 3.7032843, 21.39327, -8.856268, 3.7445884, 21.349636, -9.2248955, 3.7364876, 21.514492, -9.123741, 3.6469016, 21.423038, -9.097656, 3.8193562, 21.5222, -9.2248955, 3.7364876, 21.514492, -9.181778, 3.6451564, 21.437378, -9.123741, 3.6469016, 21.423038, -9.2248955, 3.7364876, 21.514492, -9.379068, 3.8438268, 21.68758, -9.29739, 3.622695, 21.467472, -9.529911, 3.6422298, 21.627573, -9.29739, 3.622695, 21.467472, -9.379068, 3.8438268, 21.68758, -9.529911, 3.6422298, 21.627573, -9.471037, 3.5795534, 21.537083, -9.29739, 3.622695, 21.467472, -7.801645, 4.134115, 21.58577, -7.6006274, 4.02651, 21.917599, -7.6581864, 4.072356, 21.931242, -7.801645, 4.134115, 21.58577, -7.559606, 3.912532, 21.485746, -7.6006274, 4.02651, 21.917599, -8.072794, 3.9650502, 21.715675, -7.866752, 4.057515, 21.744825, -7.952191, 3.99135, 21.80066, -8.072794, 3.9650502, 21.715675, -7.801645, 4.134115, 21.58577, -7.866752, 4.057515, 21.744825, -8.445419, 3.1368337, 20.389019, -8.276415, 3.4964035, 20.596956, -8.461891, 3.4085104, 20.773108, -8.445419, 3.1368337, 20.389019, -8.407898, 3.1417727, 20.358862, -8.276415, 3.4964035, 20.596956, -8.276415, 3.4964035, 20.596956, -8.453417, 3.4740689, 20.868896, -8.461891, 3.4085104, 20.773108, -8.276415, 3.4964035, 20.596956, -8.327274, 3.583331, 20.814444, -8.453417, 3.4740689, 20.868896, -8.327274, 3.583331, 20.814444, -8.276415, 3.4964035, 20.596956, -8.134125, 3.821342, 20.866423, -8.327274, 3.583331, 20.814444, -8.134125, 3.821342, 20.866423, -8.4303055, 3.644507, 21.12673, -8.327274, 3.583331, 20.814444, -8.4303055, 3.644507, 21.12673, -8.445759, 3.5321927, 20.955154, -8.4303055, 3.644507, 21.12673, -8.320477, 3.7492816, 21.144423, -8.409815, 3.665008, 21.169518, -8.4303055, 3.644507, 21.12673, -8.134125, 3.821342, 20.866423, -8.320477, 3.7492816, 21.144423, -8.287462, 3.8450718, 21.426939, -8.086512, 3.9769306, 21.499489, -8.231916, 3.8731332, 21.501694, -8.287462, 3.8450718, 21.426939, -7.9577355, 4.0573144, 21.235031, -8.086512, 3.9769306, 21.499489, -8.362757, 3.127033, 20.352093, -8.276415, 3.4964035, 20.596956, -8.407898, 3.1417727, 20.358862, -8.276415, 3.4964035, 20.596956, -8.362757, 3.127033, 20.352093, -8.265045, 3.3001463, 20.48862, -8.265045, 3.3001463, 20.48862, -8.362757, 3.127033, 20.352093, -8.229216, 3.2149067, 20.450375, -8.276415, 3.4964035, 20.596956, -8.026518, 3.5625966, 20.730312, -8.134125, 3.821342, 20.866423, -8.276415, 3.4964035, 20.596956, -8.06389, 3.4651048, 20.64575, -8.026518, 3.5625966, 20.730312, -7.781936, 3.5772128, 20.836325, -7.9577355, 4.0573144, 21.235031, -8.134125, 3.821342, 20.866423, -7.9577355, 4.0573144, 21.235031, -7.781936, 3.5772128, 20.836325, -7.705567, 3.6661596, 20.99604, -7.9577355, 4.0573144, 21.235031, -7.705567, 3.6661596, 20.99604, -7.7892184, 3.8130858, 21.079828, -7.7676377, 4.0055275, 21.369812, -7.801645, 4.134115, 21.58577, -7.9577355, 4.0573144, 21.235031, -7.801645, 4.134115, 21.58577, -7.7676377, 4.0055275, 21.369812, -7.5783267, 3.8855352, 21.404331, -7.801645, 4.134115, 21.58577, -7.5783267, 3.8855352, 21.404331, -7.559606, 3.912532, 21.485746, -7.9577355, 4.0573144, 21.235031, -8.320477, 3.7492816, 21.144423, -8.134125, 3.821342, 20.866423, -8.143465, 3.9182801, 21.225508, -8.320477, 3.7492816, 21.144423, -7.9577355, 4.0573144, 21.235031, -8.320477, 3.7492816, 21.144423, -8.143465, 3.9182801, 21.225508, -8.340118, 3.7362518, 21.314922, -8.320477, 3.7492816, 21.144423, -8.340118, 3.7362518, 21.314922, -8.375259, 3.7055726, 21.241833, -7.9577355, 4.0573144, 21.235031, -8.325062, 3.7988772, 21.348167, -8.143465, 3.9182801, 21.225508, -7.9577355, 4.0573144, 21.235031, -8.287462, 3.8450718, 21.426939, -8.325062, 3.7988772, 21.348167, -7.7892184, 3.8130858, 21.079828, -7.6383195, 3.7634745, 21.137505, -7.9577355, 4.0573144, 21.235031, -7.7892184, 3.8130858, 21.079828, -7.6709933, 3.7234988, 21.069057, -7.6383195, 3.7634745, 21.137505, -7.801645, 4.134115, 21.58577, -8.086512, 3.9769306, 21.499489, -7.9577355, 4.0573144, 21.235031, -8.086512, 3.9769306, 21.499489, -7.801645, 4.134115, 21.58577, -8.072794, 3.9650502, 21.715675, -8.086512, 3.9769306, 21.499489, -8.072794, 3.9650502, 21.715675, -8.151386, 3.9267366, 21.61004, -7.7676377, 4.0055275, 21.369812, -7.6383195, 3.7634745, 21.137505, -7.588919, 3.823454, 21.350895, -7.7676377, 4.0055275, 21.369812, -7.9577355, 4.0573144, 21.235031, -7.6383195, 3.7634745, 21.137505, -7.6581864, 4.072356, 21.931242, -7.866752, 4.057515, 21.744825, -7.801645, 4.134115, 21.58577, -7.866752, 4.057515, 21.744825, -7.6581864, 4.072356, 21.931242, -7.71435, 4.039794, 21.968426, -7.866752, 4.057515, 21.744825, -7.71435, 4.039794, 21.968426, -7.87205, 4.018237, 21.856892, -8.026518, 3.5625966, 20.730312, -7.781936, 3.5772128, 20.836325, -8.134125, 3.821342, 20.866423, -8.026518, 3.5625966, 20.730312, -7.8797107, 3.5000882, 20.74282, -7.781936, 3.5772128, 20.836325, -8.06389, 3.4651048, 20.64575, -7.94037, 3.4260588, 20.685284, -8.026518, 3.5625966, 20.730312, -8.06389, 3.4651048, 20.64575, -7.975001, 3.40911, 20.652027, -7.94037, 3.4260588, 20.685284, -8.06389, 3.4651048, 20.64575, -8.276415, 3.4964035, 20.596956, -8.0436325, 3.3592296, 20.586334, -8.265045, 3.3001463, 20.48862, -8.0436325, 3.3592296, 20.586334, -8.276415, 3.4964035, 20.596956, -8.265045, 3.3001463, 20.48862, -8.163176, 3.2732167, 20.498547, -8.0436325, 3.3592296, 20.586334, -7.402509, 4.1859736, 21.63811, -7.6918817, 4.041251, 21.991617, -7.7346835, 4.0696325, 21.918596, -7.402509, 4.1859736, 21.63811, -7.1659408, 4.0452685, 21.938877, -7.6918817, 4.041251, 21.991617, -7.544909, 3.8685284, 21.406876, -7.563997, 4.036748, 21.631306, -7.6227355, 3.9156742, 21.563887, -7.544909, 3.8685284, 21.406876, -7.402509, 4.1859736, 21.63811, -7.563997, 4.036748, 21.631306, -5.9238067, 3.1665137, 20.728191, -6.2251096, 3.5616388, 20.915148, -6.4278474, 3.3525786, 20.777048, -5.9238067, 3.1665137, 20.728191, -5.8848615, 3.193083, 20.760527, -6.2251096, 3.5616388, 20.915148, -6.2251096, 3.5616388, 20.915148, -6.5504594, 3.402594, 20.803858, -6.4278474, 3.3525786, 20.777048, -6.2251096, 3.5616388, 20.915148, -6.4983582, 3.5830135, 20.909115, -6.5504594, 3.402594, 20.803858, -6.4983582, 3.5830135, 20.909115, -6.2251096, 3.5616388, 20.915148, -6.5891337, 3.903202, 21.097126, -6.4983582, 3.5830135, 20.909115, -6.5891337, 3.903202, 21.097126, -6.879663, 3.532806, 20.876286, -6.4983582, 3.5830135, 20.909115, -6.879663, 3.532806, 20.876286, -6.6607127, 3.447071, 20.828083, -6.879663, 3.532806, 20.876286, -6.9066834, 3.6857386, 20.988258, -6.9293284, 3.5507002, 20.90792, -6.879663, 3.532806, 20.876286, -6.5891337, 3.903202, 21.097126, -6.9066834, 3.6857386, 20.988258, -7.2366023, 3.7330508, 21.09622, -7.3143077, 3.9347124, 21.32243, -7.315949, 3.763792, 21.176712, -7.2366023, 3.7330508, 21.09622, -7.02702, 4.1361513, 21.366934, -7.3143077, 3.9347124, 21.32243, -5.862766, 3.1931438, 20.813963, -6.2251096, 3.5616388, 20.915148, -5.8848615, 3.193083, 20.760527, -6.2251096, 3.5616388, 20.915148, -5.862766, 3.1931438, 20.813963, -6.043155, 3.376487, 20.935175, -6.043155, 3.376487, 20.935175, -5.862766, 3.1931438, 20.813963, -5.9688907, 3.3041463, 20.98022, -6.2251096, 3.5616388, 20.915148, -6.3340783, 3.6836305, 21.23523, -6.5891337, 3.903202, 21.097126, -6.2251096, 3.5616388, 20.915148, -6.2198997, 3.5840178, 21.185366, -6.3340783, 3.6836305, 21.23523, -6.400766, 3.7538157, 21.548512, -7.02702, 4.1361513, 21.366934, -6.5891337, 3.903202, 21.097126, -7.02702, 4.1361513, 21.366934, -6.400766, 3.7538157, 21.548512, -6.588054, 3.8369021, 21.666204, -7.02702, 4.1361513, 21.366934, -6.588054, 3.8369021, 21.666204, -6.7412972, 3.9567418, 21.567703, -7.1137557, 4.110402, 21.642786, -7.402509, 4.1859736, 21.63811, -7.02702, 4.1361513, 21.366934, -7.402509, 4.1859736, 21.63811, -7.1137557, 4.110402, 21.642786, -7.0714173, 4.029825, 21.898872, -7.402509, 4.1859736, 21.63811, -7.0714173, 4.029825, 21.898872, -7.1659408, 4.0452685, 21.938877, -7.02702, 4.1361513, 21.366934, -6.9066834, 3.6857386, 20.988258, -6.5891337, 3.903202, 21.097126, -7.0096154, 3.9184318, 21.182732, -6.9066834, 3.6857386, 20.988258, -7.02702, 4.1361513, 21.366934, -6.9066834, 3.6857386, 20.988258, -7.0096154, 3.9184318, 21.182732, -7.098456, 3.6138377, 21.015465, -6.9066834, 3.6857386, 20.988258, -7.098456, 3.6138377, 21.015465, -7.014134, 3.5885832, 20.961206, -7.02702, 4.1361513, 21.366934, -7.1437945, 3.6882315, 21.038242, -7.0096154, 3.9184318, 21.182732, -7.02702, 4.1361513, 21.366934, -7.2366023, 3.7330508, 21.09622, -7.1437945, 3.6882315, 21.038242, -6.7412972, 3.9567418, 21.567703, -6.756685, 3.9355235, 21.769691, -7.02702, 4.1361513, 21.366934, -6.7412972, 3.9567418, 21.567703, -6.6761546, 3.8968792, 21.71939, -6.756685, 3.9355235, 21.769691, -7.402509, 4.1859736, 21.63811, -7.3143077, 3.9347124, 21.32243, -7.02702, 4.1361513, 21.366934, -7.3143077, 3.9347124, 21.32243, -7.402509, 4.1859736, 21.63811, -7.544909, 3.8685284, 21.406876, -7.3143077, 3.9347124, 21.32243, -7.544909, 3.8685284, 21.406876, -7.432817, 3.8261418, 21.293098, -7.1137557, 4.110402, 21.642786, -6.756685, 3.9355235, 21.769691, -7.002805, 3.9603796, 21.87517, -7.1137557, 4.110402, 21.642786, -7.02702, 4.1361513, 21.366934, -6.756685, 3.9355235, 21.769691, -7.7346835, 4.0696325, 21.918596, -7.563997, 4.036748, 21.631306, -7.402509, 4.1859736, 21.63811, -7.563997, 4.036748, 21.631306, -7.7346835, 4.0696325, 21.918596, -7.775924, 4.0044317, 21.873682, -7.563997, 4.036748, 21.631306, -7.775924, 4.0044317, 21.873682, -7.6754994, 3.960377, 21.667994, -6.3340783, 3.6836305, 21.23523, -6.400766, 3.7538157, 21.548512, -6.5891337, 3.903202, 21.097126, -6.3340783, 3.6836305, 21.23523, -6.2958436, 3.6586976, 21.422325, -6.400766, 3.7538157, 21.548512, -6.2198997, 3.5840178, 21.185366, -6.2276845, 3.568183, 21.344408, -6.3340783, 3.6836305, 21.23523, -6.2198997, 3.5840178, 21.185366, -6.1918397, 3.5477042, 21.29962, -6.2276845, 3.568183, 21.344408, -6.2198997, 3.5840178, 21.185366, -6.2251096, 3.5616388, 20.915148, -6.1187816, 3.4875863, 21.210886, -6.043155, 3.376487, 20.935175, -6.1187816, 3.4875863, 21.210886, -6.2251096, 3.5616388, 20.915148, -6.043155, 3.376487, 20.935175, -6.0228896, 3.3768027, 21.062256, -6.1187816, 3.4875863, 21.210886, -7.5309367, 4.1860266, 22.233109, -7.7731357, 4.049283, 21.837759, -7.686767, 4.0676775, 21.824697, -7.5309367, 4.1860266, 22.233109, -7.90918, 4.0913115, 22.343561, -7.7731357, 4.049283, 21.837759, -7.2945504, 3.8389473, 22.203506, -7.4850507, 4.0242515, 22.093157, -7.4099345, 3.8931296, 22.069782, -7.2945504, 3.8389473, 22.203506, -7.5309367, 4.1860266, 22.233109, -7.4850507, 4.0242515, 22.093157, -7.231361, 3.2090497, 23.990551, -7.2794967, 3.594735, 23.623615, -7.099934, 3.3586783, 23.498016, -7.231361, 3.2090497, 23.990551, -7.2722163, 3.2413416, 24.013079, -7.2794967, 3.594735, 23.623615, -7.2794967, 3.594735, 23.623615, -7.0818753, 3.401124, 23.37233, -7.099934, 3.3586783, 23.498016, -7.2794967, 3.594735, 23.623615, -7.1849923, 3.5940344, 23.37246, -7.0818753, 3.401124, 23.37233, -7.1849923, 3.5940344, 23.37246, -7.2794967, 3.594735, 23.623615, -7.3083177, 3.9218946, 23.202938, -7.1849923, 3.5940344, 23.37246, -7.3083177, 3.9218946, 23.202938, -7.0331464, 3.5110173, 23.034817, -7.1849923, 3.5940344, 23.37246, -7.0331464, 3.5110173, 23.034817, -7.0656457, 3.4388018, 23.25929, -7.0331464, 3.5110173, 23.034817, -7.121306, 3.6710267, 22.96056, -7.043958, 3.527542, 22.976107, -7.0331464, 3.5110173, 23.034817, -7.3083177, 3.9218946, 23.202938, -7.121306, 3.6710267, 22.96056, -7.113857, 3.7015529, 22.612543, -7.287684, 3.915894, 22.445786, -7.160318, 3.7328143, 22.507229, -7.113857, 3.7015529, 22.612543, -7.4040046, 4.1427026, 22.68545, -7.287684, 3.915894, 22.445786, -7.3301635, 3.2478483, 24.013186, -7.2794967, 3.594735, 23.623615, -7.2722163, 3.2413416, 24.013079, -7.2794967, 3.594735, 23.623615, -7.3301635, 3.2478483, 24.013186, -7.371898, 3.4266107, 23.792282, -7.371898, 3.4266107, 23.792282, -7.3301635, 3.2478483, 24.013186, -7.442037, 3.3642597, 23.847004, -7.2794967, 3.594735, 23.623615, -7.540323, 3.735655, 23.39579, -7.3083177, 3.9218946, 23.202938, -7.2794967, 3.594735, 23.623615, -7.5373693, 3.641142, 23.524654, -7.540323, 3.735655, 23.39579, -7.8095174, 3.8275523, 23.211908, -7.4040046, 4.1427026, 22.68545, -7.3083177, 3.9218946, 23.202938, -7.4040046, 4.1427026, 22.68545, -7.8095174, 3.8275523, 23.211908, -7.8509803, 3.9055398, 22.990408, -7.4040046, 4.1427026, 22.68545, -7.8509803, 3.9055398, 22.990408, -7.703343, 4.004422, 22.88101, -7.6422763, 4.1345253, 22.501507, -7.5309367, 4.1860266, 22.233109, -7.4040046, 4.1427026, 22.68545, -7.5309367, 4.1860266, 22.233109, -7.6422763, 4.1345253, 22.501507, -7.905112, 4.080037, 22.446768, -7.5309367, 4.1860266, 22.233109, -7.905112, 4.080037, 22.446768, -7.90918, 4.0913115, 22.343561, -7.4040046, 4.1427026, 22.68545, -7.121306, 3.6710267, 22.96056, -7.3083177, 3.9218946, 23.202938, -7.255689, 3.9115663, 22.781116, -7.121306, 3.6710267, 22.96056, -7.4040046, 4.1427026, 22.68545, -7.121306, 3.6710267, 22.96056, -7.255689, 3.9115663, 22.781116, -7.080893, 3.5860498, 22.77619, -7.121306, 3.6710267, 22.96056, -7.080893, 3.5860498, 22.77619, -7.062715, 3.563114, 22.87582, -7.4040046, 4.1427026, 22.68545, -7.0931907, 3.6592827, 22.722273, -7.255689, 3.9115663, 22.781116, -7.4040046, 4.1427026, 22.68545, -7.113857, 3.7015529, 22.612543, -7.0931907, 3.6592827, 22.722273, -7.703343, 4.004422, 22.88101, -7.8892875, 3.9995782, 22.79086, -7.4040046, 4.1427026, 22.68545, -7.703343, 4.004422, 22.88101, -7.8713326, 3.9631367, 22.88616, -7.8892875, 3.9995782, 22.79086, -7.5309367, 4.1860266, 22.233109, -7.287684, 3.915894, 22.445786, -7.4040046, 4.1427026, 22.68545, -7.287684, 3.915894, 22.445786, -7.5309367, 4.1860266, 22.233109, -7.2945504, 3.8389473, 22.203506, -7.287684, 3.915894, 22.445786, -7.2945504, 3.8389473, 22.203506, -7.2290454, 3.7958574, 22.35221, -7.6422763, 4.1345253, 22.501507, -7.8892875, 3.9995782, 22.79086, -7.8991284, 4.0135202, 22.522274, -7.6422763, 4.1345253, 22.501507, -7.4040046, 4.1427026, 22.68545, -7.8892875, 3.9995782, 22.79086, -7.686767, 4.0676775, 21.824697, -7.4850507, 4.0242515, 22.093157, -7.5309367, 4.1860266, 22.233109, -7.4850507, 4.0242515, 22.093157, -7.686767, 4.0676775, 21.824697, -7.63722, 3.9957073, 21.806574, -7.4850507, 4.0242515, 22.093157, -7.63722, 3.9957073, 21.806574, -7.487868, 3.9425092, 21.979464, -7.540323, 3.735655, 23.39579, -7.8095174, 3.8275523, 23.211908, -7.3083177, 3.9218946, 23.202938, -7.540323, 3.735655, 23.39579, -7.7316575, 3.7301946, 23.361069, -7.8095174, 3.8275523, 23.211908, -7.5373693, 3.641142, 23.524654, -7.680619, 3.6382384, 23.457644, -7.540323, 3.735655, 23.39579, -7.5373693, 3.641142, 23.524654, -7.6541924, 3.6169612, 23.508732, -7.680619, 3.6382384, 23.457644, -7.5373693, 3.641142, 23.524654, -7.2794967, 3.594735, 23.623615, -7.599995, 3.5552611, 23.612686, -7.371898, 3.4266107, 23.792282, -7.599995, 3.5552611, 23.612686, -7.2794967, 3.594735, 23.623615, -7.371898, 3.4266107, 23.792282, -7.4989257, 3.4396398, 23.762728, -7.599995, 3.5552611, 23.612686, -6.9064727, 0.8870442, 21.944698, -7.0634227, 1.9859911, 21.955528, -6.983601, 1.9958726, 21.857681, -6.9064727, 0.8870442, 21.944698, -7.0050845, 0.88108003, 22.061377, -7.0634227, 1.9859911, 21.955528, -7.007685, -0.32457638, 22.15034, -7.0050845, 0.88108003, 22.061377, -6.9064727, 0.8870442, 21.944698, -7.007685, -0.32457638, 22.15034, -7.116863, -0.30735517, 22.281223, -7.0050845, 0.88108003, 22.061377, -7.2671094, 0.85050464, 21.996153, -7.3045907, 1.9478768, 21.77239, -7.284966, 1.9531578, 21.8974, -7.2671094, 0.85050464, 21.996153, -7.288546, 0.84261394, 21.850367, -7.3045907, 1.9478768, 21.77239, -7.0425286, 0.86357975, 21.707298, -7.003896, 1.9904655, 21.73263, -7.101132, 1.9756031, 21.652607, -7.0425286, 0.86357975, 21.707298, -6.925528, 0.87903726, 21.79757, -7.003896, 1.9904655, 21.73263, -7.284966, 1.9531578, 21.8974, -7.4806666, 2.9942842, 21.815382, -7.466662, 2.9961905, 21.89844, -7.284966, 1.9531578, 21.8974, -7.3045907, 1.9478768, 21.77239, -7.4806666, 2.9942842, 21.815382, -6.983601, 1.9958726, 21.857681, -7.320312, 3.0230846, 21.937897, -7.2666173, 3.0324101, 21.872292, -6.983601, 1.9958726, 21.857681, -7.0634227, 1.9859911, 21.955528, -7.320312, 3.0230846, 21.937897, -7.2666173, 3.0324101, 21.872292, -7.320312, 3.0230846, 21.937897, -7.4912467, 3.573497, 21.949327, -7.2666173, 3.0324101, 21.872292, -7.4912467, 3.573497, 21.949327, -7.453357, 3.5873647, 21.900778, -7.0050845, 0.88108003, 22.061377, -7.1874385, 1.968081, 21.976803, -7.0634227, 1.9859911, 21.955528, -7.0050845, 0.88108003, 22.061377, -7.15249, 0.8660785, 22.087767, -7.1874385, 1.968081, 21.976803, -7.0634227, 1.9859911, 21.955528, -7.1874385, 1.968081, 21.976803, -7.40249, 3.0077667, 21.95179, -7.0634227, 1.9859911, 21.955528, -7.40249, 3.0077667, 21.95179, -7.320312, 3.0230846, 21.937897, -7.288546, 0.84261394, 21.850367, -7.2247424, 1.9581697, 21.674047, -7.3045907, 1.9478768, 21.77239, -7.288546, 0.84261394, 21.850367, -7.192162, 0.8488163, 21.734951, -7.2247424, 1.9581697, 21.674047, -7.3045907, 1.9478768, 21.77239, -7.2247424, 1.9581697, 21.674047, -7.428226, 3.0044737, 21.749603, -7.3045907, 1.9478768, 21.77239, -7.428226, 3.0044737, 21.749603, -7.4806666, 2.9942842, 21.815382, -7.101132, 1.9756031, 21.652607, -7.2801204, 3.029789, 21.78821, -7.345489, 3.018808, 21.73467, -7.101132, 1.9756031, 21.652607, -7.003896, 1.9904655, 21.73263, -7.2801204, 3.029789, 21.78821, -6.925528, 0.87903726, 21.79757, -6.983601, 1.9958726, 21.857681, -7.003896, 1.9904655, 21.73263, -6.925528, 0.87903726, 21.79757, -6.9064727, 0.8870442, 21.944698, -6.983601, 1.9958726, 21.857681, -7.003896, 1.9904655, 21.73263, -7.2666173, 3.0324101, 21.872292, -7.2801204, 3.029789, 21.78821, -7.003896, 1.9904655, 21.73263, -6.983601, 1.9958726, 21.857681, -7.2666173, 3.0324101, 21.872292, -7.192162, 0.8488163, 21.734951, -7.101132, 1.9756031, 21.652607, -7.2247424, 1.9581697, 21.674047, -7.192162, 0.8488163, 21.734951, -7.0425286, 0.86357975, 21.707298, -7.101132, 1.9756031, 21.652607, -7.3367667, -0.3452804, 21.89752, -7.0425286, 0.86357975, 21.707298, -7.192162, 0.8488163, 21.734951, -7.3367667, -0.3452804, 21.89752, -7.1676164, -0.3521583, 21.871283, -7.0425286, 0.86357975, 21.707298, -7.2247424, 1.9581697, 21.674047, -7.345489, 3.018808, 21.73467, -7.428226, 3.0044737, 21.749603, -7.2247424, 1.9581697, 21.674047, -7.101132, 1.9756031, 21.652607, -7.345489, 3.018808, 21.73467, -7.428226, 3.0044737, 21.749603, -7.345489, 3.018808, 21.73467, -7.5119376, 3.577262, 21.798561, -7.428226, 3.0044737, 21.749603, -7.5119376, 3.577262, 21.798561, -7.5716386, 3.5596325, 21.809055, -7.5982623, 3.5452437, 21.919594, -7.6945357, 3.969666, 21.982683, -7.5509553, 3.5562305, 21.959482, -7.5982623, 3.5452437, 21.919594, -7.7289567, 3.960751, 21.953426, -7.6945357, 3.969666, 21.982683, -7.4912467, 3.573497, 21.949327, -7.6261945, 4.0009108, 21.940428, -7.453357, 3.5873647, 21.900778, -7.4912467, 3.573497, 21.949327, -7.6525865, 3.9872801, 21.975618, -7.6261945, 4.0009108, 21.940428, -7.5509553, 3.5562305, 21.959482, -7.6525865, 3.9872801, 21.975618, -7.4912467, 3.573497, 21.949327, -7.5509553, 3.5562305, 21.959482, -7.6945357, 3.969666, 21.982683, -7.6525865, 3.9872801, 21.975618, -7.4642043, 3.588051, 21.838531, -7.668904, 3.9935448, 21.865906, -7.5119376, 3.577262, 21.798561, -7.4642043, 3.588051, 21.838531, -7.634957, 4.0036774, 21.895311, -7.668904, 3.9935448, 21.865906, -7.453357, 3.5873647, 21.900778, -7.634957, 4.0036774, 21.895311, -7.4642043, 3.588051, 21.838531, -7.453357, 3.5873647, 21.900778, -7.6261945, 4.0009108, 21.940428, -7.634957, 4.0036774, 21.895311, -7.5716386, 3.5596325, 21.809055, -7.737245, 3.9623003, 21.908161, -7.6092744, 3.5461726, 21.857725, -7.5716386, 3.5596325, 21.809055, -7.711328, 3.9771495, 21.873121, -7.737245, 3.9623003, 21.908161, -7.5119376, 3.577262, 21.798561, -7.711328, 3.9771495, 21.873121, -7.5716386, 3.5596325, 21.809055, -7.5119376, 3.577262, 21.798561, -7.668904, 3.9935448, 21.865906, -7.711328, 3.9771495, 21.873121, -7.2869067, -0.3004067, 22.307976, -7.2671094, 0.85050464, 21.996153, -7.15249, 0.8660785, 22.087767, -7.2869067, -0.3004067, 22.307976, -7.4190135, -0.30967426, 22.197882, -7.2671094, 0.85050464, 21.996153, -7.6092744, 3.5461726, 21.857725, -7.7289567, 3.960751, 21.953426, -7.5982623, 3.5452437, 21.919594, -7.6092744, 3.5461726, 21.857725, -7.737245, 3.9623003, 21.908161, -7.7289567, 3.960751, 21.953426, -7.15249, 0.8660785, 22.087767, -7.284966, 1.9531578, 21.8974, -7.1874385, 1.968081, 21.976803, -7.15249, 0.8660785, 22.087767, -7.2671094, 0.85050464, 21.996153, -7.284966, 1.9531578, 21.8974, -7.4190135, -0.30967426, 22.197882, -7.288546, 0.84261394, 21.850367, -7.2671094, 0.85050464, 21.996153, -7.4190135, -0.30967426, 22.197882, -7.446838, -0.32798862, 22.028917, -7.288546, 0.84261394, 21.850367, -7.1676164, -0.3521583, 21.871283, -6.925528, 0.87903726, 21.79757, -7.0425286, 0.86357975, 21.707298, -7.1676164, -0.3521583, 21.871283, -7.036158, -0.3428421, 21.981743, -6.925528, 0.87903726, 21.79757, -7.116863, -0.30735517, 22.281223, -7.15249, 0.8660785, 22.087767, -7.0050845, 0.88108003, 22.061377, -7.116863, -0.30735517, 22.281223, -7.2869067, -0.3004067, 22.307976, -7.15249, 0.8660785, 22.087767, -7.446838, -0.32798862, 22.028917, -7.192162, 0.8488163, 21.734951, -7.288546, 0.84261394, 21.850367, -7.446838, -0.32798862, 22.028917, -7.3367667, -0.3452804, 21.89752, -7.192162, 0.8488163, 21.734951, -7.036158, -0.3428421, 21.981743, -6.9064727, 0.8870442, 21.944698, -6.925528, 0.87903726, 21.79757, -7.036158, -0.3428421, 21.981743, -7.007685, -0.32457638, 22.15034, -6.9064727, 0.8870442, 21.944698, -7.1874385, 1.968081, 21.976803, -7.466662, 2.9961905, 21.89844, -7.40249, 3.0077667, 21.95179, -7.1874385, 1.968081, 21.976803, -7.284966, 1.9531578, 21.8974, -7.466662, 2.9961905, 21.89844, -7.40249, 3.0077667, 21.95179, -7.5982623, 3.5452437, 21.919594, -7.5509553, 3.5562305, 21.959482, -7.40249, 3.0077667, 21.95179, -7.466662, 2.9961905, 21.89844, -7.5982623, 3.5452437, 21.919594, -7.466662, 2.9961905, 21.89844, -7.6092744, 3.5461726, 21.857725, -7.5982623, 3.5452437, 21.919594, -7.466662, 2.9961905, 21.89844, -7.4806666, 2.9942842, 21.815382, -7.6092744, 3.5461726, 21.857725, -7.320312, 3.0230846, 21.937897, -7.5509553, 3.5562305, 21.959482, -7.4912467, 3.573497, 21.949327, -7.320312, 3.0230846, 21.937897, -7.40249, 3.0077667, 21.95179, -7.5509553, 3.5562305, 21.959482, -7.345489, 3.018808, 21.73467, -7.4642043, 3.588051, 21.838531, -7.5119376, 3.577262, 21.798561, -7.345489, 3.018808, 21.73467, -7.2801204, 3.029789, 21.78821, -7.4642043, 3.588051, 21.838531, -7.2801204, 3.029789, 21.78821, -7.453357, 3.5873647, 21.900778, -7.4642043, 3.588051, 21.838531, -7.2801204, 3.029789, 21.78821, -7.2666173, 3.0324101, 21.872292, -7.453357, 3.5873647, 21.900778, -7.4806666, 2.9942842, 21.815382, -7.5716386, 3.5596325, 21.809055, -7.6092744, 3.5461726, 21.857725, -7.4806666, 2.9942842, 21.815382, -7.428226, 3.0044737, 21.749603, -7.5716386, 3.5596325, 21.809055, -25.120148, 13.783545, 12.764626, -26.397978, 13.369994, 12.574373, -26.33375, 13.5643215, 12.432411, -25.120148, 13.783545, 12.764626, -25.263885, 12.827821, 13.423475, -26.397978, 13.369994, 12.574373, -24.933325, 13.512459, 11.685724, -25.424961, 13.642609, 12.2283325, -25.402243, 13.524235, 11.855768, -24.933325, 13.512459, 11.685724, -25.120148, 13.783545, 12.764626, -25.424961, 13.642609, 12.2283325, -20.327318, 10.662638, 12.792898, -21.313543, 11.772419, 13.055622, -21.432056, 11.691162, 12.167927, -20.327318, 10.662638, 12.792898, -20.315731, 10.639986, 12.953346, -21.313543, 11.772419, 13.055622, -21.313543, 11.772419, 13.055622, -21.73093, 11.930523, 12.043638, -21.432056, 11.691162, 12.167927, -21.313543, 11.772419, 13.055622, -21.845343, 12.171588, 12.549276, -21.73093, 11.930523, 12.043638, -21.845343, 12.171588, 12.549276, -21.313543, 11.772419, 13.055622, -22.412296, 12.812631, 13.106188, -21.845343, 12.171588, 12.549276, -22.412296, 12.812631, 13.106188, -22.532686, 12.553142, 11.708666, -21.845343, 12.171588, 12.549276, -22.532686, 12.553142, 11.708666, -21.999557, 12.142736, 11.931597, -22.532686, 12.553142, 11.708666, -22.81894, 12.8182955, 12.022795, -22.69415, 12.618034, 11.694317, -22.532686, 12.553142, 11.708666, -22.412296, 12.812631, 13.106188, -22.81894, 12.8182955, 12.022795, -23.693872, 13.206497, 11.613611, -24.30996, 13.49691, 12.078384, -24.013233, 13.277237, 11.631985, -23.693872, 13.206497, 11.613611, -23.830704, 13.556321, 12.993084, -24.30996, 13.49691, 12.078384, -20.380512, 10.551989, 13.072411, -21.313543, 11.772419, 13.055622, -20.315731, 10.639986, 12.953346, -21.313543, 11.772419, 13.055622, -20.380512, 10.551989, 13.072411, -20.98937, 11.087692, 13.168047, -20.98937, 11.087692, 13.168047, -20.380512, 10.551989, 13.072411, -20.92976, 10.772113, 13.284454, -21.313543, 11.772419, 13.055622, -22.184439, 11.839201, 13.474684, -22.412296, 12.812631, 13.106188, -21.313543, 11.772419, 13.055622, -21.854174, 11.521826, 13.480944, -22.184439, 11.839201, 13.474684, -22.953316, 11.729006, 13.905809, -23.830704, 13.556321, 12.993084, -22.412296, 12.812631, 13.106188, -23.830704, 13.556321, 12.993084, -22.953316, 11.729006, 13.905809, -23.56244, 12.014381, 13.853283, -23.830704, 13.556321, 12.993084, -23.56244, 12.014381, 13.853283, -23.671707, 12.578844, 13.562656, -24.563269, 13.266899, 13.176035, -25.120148, 13.783545, 12.764626, -23.830704, 13.556321, 12.993084, -25.120148, 13.783545, 12.764626, -24.563269, 13.266899, 13.176035, -24.997204, 12.729578, 13.513615, -25.120148, 13.783545, 12.764626, -24.997204, 12.729578, 13.513615, -25.263885, 12.827821, 13.423475, -23.830704, 13.556321, 12.993084, -22.81894, 12.8182955, 12.022795, -22.412296, 12.812631, 13.106188, -23.423197, 13.259348, 12.431152, -22.81894, 12.8182955, 12.022795, -23.830704, 13.556321, 12.993084, -22.81894, 12.8182955, 12.022795, -23.423197, 13.259348, 12.431152, -23.243961, 12.84373, 11.64538, -22.81894, 12.8182955, 12.022795, -23.243961, 12.84373, 11.64538, -22.970037, 12.747108, 11.67061, -23.830704, 13.556321, 12.993084, -23.392378, 13.061067, 11.639456, -23.423197, 13.259348, 12.431152, -23.830704, 13.556321, 12.993084, -23.693872, 13.206497, 11.613611, -23.392378, 13.061067, 11.639456, -23.671707, 12.578844, 13.562656, -24.109556, 12.314231, 13.807953, -23.830704, 13.556321, 12.993084, -23.671707, 12.578844, 13.562656, -23.848108, 12.188936, 13.830387, -24.109556, 12.314231, 13.807953, -25.120148, 13.783545, 12.764626, -24.30996, 13.49691, 12.078384, -23.830704, 13.556321, 12.993084, -24.30996, 13.49691, 12.078384, -25.120148, 13.783545, 12.764626, -24.933325, 13.512459, 11.685724, -24.30996, 13.49691, 12.078384, -24.933325, 13.512459, 11.685724, -24.482748, 13.423203, 11.660212, -24.563269, 13.266899, 13.176035, -24.109556, 12.314231, 13.807953, -24.80421, 12.541008, 13.571323, -24.563269, 13.266899, 13.176035, -23.830704, 13.556321, 12.993084, -24.109556, 12.314231, 13.807953, -26.33375, 13.5643215, 12.432411, -25.424961, 13.642609, 12.2283325, -25.120148, 13.783545, 12.764626, -25.424961, 13.642609, 12.2283325, -26.33375, 13.5643215, 12.432411, -26.324017, 13.5112915, 12.189222, -25.424961, 13.642609, 12.2283325, -26.324017, 13.5112915, 12.189222, -25.71668, 13.54311, 11.969755, -22.184439, 11.839201, 13.474684, -22.953316, 11.729006, 13.905809, -22.412296, 12.812631, 13.106188, -22.184439, 11.839201, 13.474684, -22.487688, 11.524162, 13.78611, -22.953316, 11.729006, 13.905809, -21.854174, 11.521826, 13.480944, -22.187183, 11.317971, 13.70713, -22.184439, 11.839201, 13.474684, -21.854174, 11.521826, 13.480944, -22.027325, 11.277615, 13.666721, -22.187183, 11.317971, 13.70713, -21.854174, 11.521826, 13.480944, -21.313543, 11.772419, 13.055622, -21.702065, 11.143689, 13.583382, -20.98937, 11.087692, 13.168047, -21.702065, 11.143689, 13.583382, -21.313543, 11.772419, 13.055622, -20.98937, 11.087692, 13.168047, -21.207434, 10.925812, 13.392204, -21.702065, 11.143689, 13.583382, -27.190845, 13.454124, 13.211977, -26.123392, 13.504549, 12.108877, -26.042444, 13.6264305, 12.347961, -27.190845, 13.454124, 13.211977, -27.674408, 12.802565, 12.217752, -26.123392, 13.504549, 12.108877, -26.397722, 12.685178, 13.907841, -26.517313, 13.244606, 13.225155, -26.210627, 12.959142, 13.428625, -26.397722, 12.685178, 13.907841, -27.190845, 13.454124, 13.211977, -26.517313, 13.244606, 13.225155, -30.59442, 8.298002, 15.834078, -30.123693, 9.8882065, 15.319544, -29.275402, 9.5317335, 15.74361, -30.59442, 8.298002, 15.834078, -30.741045, 8.328472, 15.732853, -30.123693, 9.8882065, 15.319544, -30.123693, 9.8882065, 15.319544, -28.95761, 9.849106, 15.67477, -29.275402, 9.5317335, 15.74361, -30.123693, 9.8882065, 15.319544, -29.313347, 10.32216, 15.361237, -28.95761, 9.849106, 15.67477, -29.313347, 10.32216, 15.361237, -30.123693, 9.8882065, 15.319544, -29.39274, 11.410282, 14.816835, -29.313347, 10.32216, 15.361237, -29.39274, 11.410282, 14.816835, -28.10266, 10.688791, 15.489471, -29.313347, 10.32216, 15.361237, -28.10266, 10.688791, 15.489471, -28.671627, 10.132956, 15.612846, -28.10266, 10.688791, 15.489471, -28.17546, 11.1942005, 15.154776, -27.974642, 10.813639, 15.397457, -28.10266, 10.688791, 15.489471, -29.39274, 11.410282, 14.816835, -28.17546, 11.1942005, 15.154776, -27.208647, 11.806034, 14.844706, -27.177315, 12.54514, 14.161594, -26.99829, 12.019685, 14.60232, -27.208647, 11.806034, 14.844706, -28.297514, 12.740184, 14.028536, -27.177315, 12.54514, 14.161594, -30.79949, 8.3112135, 15.561966, -30.123693, 9.8882065, 15.319544, -30.741045, 8.328472, 15.732853, -30.123693, 9.8882065, 15.319544, -30.79949, 8.3112135, 15.561966, -30.452688, 9.11234, 15.217451, -30.452688, 9.11234, 15.217451, -30.79949, 8.3112135, 15.561966, -30.594294, 8.811231, 15.062023, -30.123693, 9.8882065, 15.319544, -29.879786, 10.464624, 14.329517, -29.39274, 11.410282, 14.816835, -30.123693, 9.8882065, 15.319544, -30.117891, 10.010266, 14.465597, -29.879786, 10.464624, 14.329517, -29.715208, 10.829817, 13.354015, -28.297514, 12.740184, 14.028536, -29.39274, 11.410282, 14.816835, -28.297514, 12.740184, 14.028536, -29.715208, 10.829817, 13.354015, -29.23813, 11.34871, 13.010698, -28.297514, 12.740184, 14.028536, -29.23813, 11.34871, 13.010698, -28.912056, 11.883873, 13.344931, -27.949308, 12.851552, 13.156515, -27.190845, 13.454124, 13.211977, -28.297514, 12.740184, 14.028536, -27.190845, 13.454124, 13.211977, -27.949308, 12.851552, 13.156515, -27.942167, 12.621899, 12.332624, -27.190845, 13.454124, 13.211977, -27.942167, 12.621899, 12.332624, -27.674408, 12.802565, 12.217752, -28.297514, 12.740184, 14.028536, -28.17546, 11.1942005, 15.154776, -29.39274, 11.410282, 14.816835, -28.104351, 12.050874, 14.57691, -28.17546, 11.1942005, 15.154776, -28.297514, 12.740184, 14.028536, -28.17546, 11.1942005, 15.154776, -28.104351, 12.050874, 14.57691, -27.539679, 11.245704, 15.084768, -28.17546, 11.1942005, 15.154776, -27.539679, 11.245704, 15.084768, -27.758598, 11.0494, 15.242031, -28.297514, 12.740184, 14.028536, -27.44365, 11.537734, 15.013952, -28.104351, 12.050874, 14.57691, -28.297514, 12.740184, 14.028536, -27.208647, 11.806034, 14.844706, -27.44365, 11.537734, 15.013952, -28.912056, 11.883873, 13.344931, -28.817327, 11.888908, 12.70685, -28.297514, 12.740184, 14.028536, -28.912056, 11.883873, 13.344931, -29.021376, 11.656829, 12.85384, -28.817327, 11.888908, 12.70685, -27.190845, 13.454124, 13.211977, -27.177315, 12.54514, 14.161594, -28.297514, 12.740184, 14.028536, -27.177315, 12.54514, 14.161594, -27.190845, 13.454124, 13.211977, -26.397722, 12.685178, 13.907841, -27.177315, 12.54514, 14.161594, -26.397722, 12.685178, 13.907841, -26.694912, 12.386589, 14.250351, -27.949308, 12.851552, 13.156515, -28.817327, 11.888908, 12.70685, -28.11471, 12.312162, 12.404867, -27.949308, 12.851552, 13.156515, -28.297514, 12.740184, 14.028536, -28.817327, 11.888908, 12.70685, -26.042444, 13.6264305, 12.347961, -26.517313, 13.244606, 13.225155, -27.190845, 13.454124, 13.211977, -26.517313, 13.244606, 13.225155, -26.042444, 13.6264305, 12.347961, -25.841175, 13.485764, 12.483356, -26.517313, 13.244606, 13.225155, -25.841175, 13.485764, 12.483356, -26.088339, 13.183667, 13.109575, -29.879786, 10.464624, 14.329517, -29.715208, 10.829817, 13.354015, -29.39274, 11.410282, 14.816835, -29.879786, 10.464624, 14.329517, -29.939215, 10.384001, 13.731905, -29.715208, 10.829817, 13.354015, -30.117891, 10.010266, 14.465597, -30.074621, 10.006492, 13.9677925, -29.879786, 10.464624, 14.329517, -30.117891, 10.010266, 14.465597, -30.15518, 9.890993, 14.100925, -30.074621, 10.006492, 13.9677925, -30.117891, 10.010266, 14.465597, -30.123693, 9.8882065, 15.319544, -30.313383, 9.599236, 14.366161, -30.452688, 9.11234, 15.217451, -30.313383, 9.599236, 14.366161, -30.123693, 9.8882065, 15.319544, -30.452688, 9.11234, 15.217451, -30.495245, 9.115306, 14.813856, -30.313383, 9.599236, 14.366161, -27.201202, 14.111977, 12.216812, -25.942682, 13.340691, 12.239897, -25.963226, 13.52549, 12.449766, -27.201202, 14.111977, 12.216812, -27.26703, 13.264112, 11.247925, -25.942682, 13.340691, 12.239897, -27.481268, 13.527826, 13.334137, -26.931988, 13.740763, 12.7410145, -26.979942, 13.491425, 13.121708, -27.481268, 13.527826, 13.334137, -27.201202, 14.111977, 12.216812, -26.931988, 13.740763, 12.7410145, -32.707096, 11.829945, 11.838261, -31.489027, 12.834562, 11.726076, -31.396978, 12.465054, 12.665121, -32.707096, 11.829945, 11.838261, -32.720463, 11.854302, 11.658894, -31.489027, 12.834562, 11.726076, -31.489027, 12.834562, 11.726076, -31.048937, 12.614889, 12.831612, -31.396978, 12.465054, 12.665121, -31.489027, 12.834562, 11.726076, -30.883116, 12.990331, 12.325209, -31.048937, 12.614889, 12.831612, -30.883116, 12.990331, 12.325209, -31.489027, 12.834562, 11.726076, -30.172102, 13.71146, 11.822292, -30.883116, 12.990331, 12.325209, -30.172102, 13.71146, 11.822292, -30.112314, 13.00444, 13.27858, -30.883116, 12.990331, 12.325209, -30.112314, 13.00444, 13.27858, -30.735453, 12.748096, 12.981314, -30.112314, 13.00444, 13.27858, -29.77852, 13.318491, 12.969162, -29.928392, 13.031496, 13.299112, -30.112314, 13.00444, 13.27858, -30.172102, 13.71146, 11.822292, -29.77852, 13.318491, 12.969162, -28.82114, 13.425723, 13.433544, -28.131073, 13.753705, 12.945568, -28.474356, 13.438845, 13.407442, -28.82114, 13.425723, 13.433544, -28.579897, 14.188468, 12.007677, -28.131073, 13.753705, 12.945568, -32.672134, 11.781767, 11.502314, -31.489027, 12.834562, 11.726076, -32.720463, 11.854302, 11.658894, -31.489027, 12.834562, 11.726076, -32.672134, 11.781767, 11.502314, -31.952402, 12.217968, 11.469395, -31.952402, 12.217968, 11.469395, -32.672134, 11.781767, 11.502314, -32.0643, 11.928456, 11.276808, -31.489027, 12.834562, 11.726076, -30.603403, 12.827642, 11.204182, -30.172102, 13.71146, 11.822292, -31.489027, 12.834562, 11.726076, -30.9988, 12.565276, 11.150988, -30.603403, 12.827642, 11.204182, -29.840925, 12.6641445, 10.642536, -28.579897, 14.188468, 12.007677, -30.172102, 13.71146, 11.822292, -28.579897, 14.188468, 12.007677, -29.840925, 12.6641445, 10.642536, -29.154, 12.815418, 10.721668, -28.579897, 14.188468, 12.007677, -29.154, 12.815418, 10.721668, -28.948093, 13.334579, 11.159084, -27.901386, 13.802778, 11.676207, -27.201202, 14.111977, 12.216812, -28.579897, 14.188468, 12.007677, -27.201202, 14.111977, 12.216812, -27.901386, 13.802778, 11.676207, -27.566175, 13.240652, 11.143919, -27.201202, 14.111977, 12.216812, -27.566175, 13.240652, 11.143919, -27.26703, 13.264112, 11.247925, -28.579897, 14.188468, 12.007677, -29.77852, 13.318491, 12.969162, -30.172102, 13.71146, 11.822292, -29.078878, 13.783996, 12.575394, -29.77852, 13.318491, 12.969162, -28.579897, 14.188468, 12.007677, -29.77852, 13.318491, 12.969162, -29.078878, 13.783996, 12.575394, -29.3032, 13.130279, 13.369244, -29.77852, 13.318491, 12.969162, -29.3032, 13.130279, 13.369244, -29.61749, 13.100707, 13.335033, -28.579897, 14.188468, 12.007677, -29.159786, 13.339252, 13.394022, -29.078878, 13.783996, 12.575394, -28.579897, 14.188468, 12.007677, -28.82114, 13.425723, 13.433544, -29.159786, 13.339252, 13.394022, -28.948093, 13.334579, 11.159084, -28.545794, 13.027181, 10.795259, -28.579897, 14.188468, 12.007677, -28.948093, 13.334579, 11.159084, -28.840069, 12.952937, 10.761011, -28.545794, 13.027181, 10.795259, -27.201202, 14.111977, 12.216812, -28.131073, 13.753705, 12.945568, -28.579897, 14.188468, 12.007677, -28.131073, 13.753705, 12.945568, -27.201202, 14.111977, 12.216812, -27.481268, 13.527826, 13.334137, -28.131073, 13.753705, 12.945568, -27.481268, 13.527826, 13.334137, -27.971476, 13.512636, 13.371208, -27.901386, 13.802778, 11.676207, -28.545794, 13.027181, 10.795259, -27.7606, 13.039855, 11.0648365, -27.901386, 13.802778, 11.676207, -28.579897, 14.188468, 12.007677, -28.545794, 13.027181, 10.795259, -25.963226, 13.52549, 12.449766, -26.931988, 13.740763, 12.7410145, -27.201202, 14.111977, 12.216812, -26.931988, 13.740763, 12.7410145, -25.963226, 13.52549, 12.449766, -25.99063, 13.406307, 12.702139, -26.931988, 13.740763, 12.7410145, -25.99063, 13.406307, 12.702139, -26.647318, 13.508326, 12.980991, -30.603403, 12.827642, 11.204182, -29.840925, 12.6641445, 10.642536, -30.172102, 13.71146, 11.822292, -30.603403, 12.827642, 11.204182, -30.356276, 12.512424, 10.763277, -29.840925, 12.6641445, 10.642536, -30.9988, 12.565276, 11.150988, -30.676294, 12.321331, 10.836258, -30.603403, 12.827642, 11.204182, -30.9988, 12.565276, 11.150988, -30.858511, 12.308209, 10.879881, -30.676294, 12.321331, 10.836258, -30.9988, 12.565276, 11.150988, -31.489027, 12.834562, 11.726076, -31.220568, 12.222282, 10.96516, -31.952402, 12.217968, 11.469395, -31.220568, 12.222282, 10.96516, -31.489027, 12.834562, 11.726076, -31.952402, 12.217968, 11.469395, -31.764006, 12.055641, 11.166313, -31.220568, 12.222282, 10.96516, -26.549658, 13.852484, 11.521194, -25.914532, 13.414665, 12.523054, -26.09762, 13.550388, 12.580524, -26.549658, 13.852484, 11.521194, -25.778147, 13.206228, 11.136642, -25.914532, 13.414665, 12.523054, -27.393269, 13.267618, 11.867287, -26.750992, 13.556589, 11.991425, -27.016235, 13.327029, 12.142666, -27.393269, 13.267618, 11.867287, -26.549658, 13.852484, 11.521194, -26.750992, 13.556589, 11.991425, -28.51631, 11.110254, 7.4424706, -28.004425, 12.171139, 8.214411, -28.58325, 11.828327, 8.733058, -28.51631, 11.110254, 7.4424706, -28.3986, 11.137669, 7.350334, -28.004425, 12.171139, 8.214411, -28.004425, 12.171139, 8.214411, -28.560322, 12.001171, 9.0543, -28.58325, 11.828327, 8.733058, -28.004425, 12.171139, 8.214411, -28.169415, 12.366816, 8.922272, -28.560322, 12.001171, 9.0543, -28.169415, 12.366816, 8.922272, -28.004425, 12.171139, 8.214411, -27.574968, 13.101874, 9.166556, -28.169415, 12.366816, 8.922272, -27.574968, 13.101874, 9.166556, -28.497368, 12.447617, 9.9169655, -28.169415, 12.366816, 8.922272, -28.497368, 12.447617, 9.9169655, -28.539549, 12.153973, 9.343277, -28.497368, 12.447617, 9.9169655, -28.157778, 12.77416, 10.008541, -28.434235, 12.498222, 10.057671, -28.497368, 12.447617, 9.9169655, -27.574968, 13.101874, 9.166556, -28.157778, 12.77416, 10.008541, -28.060122, 12.979725, 10.923248, -27.435986, 13.376825, 11.196117, -27.887424, 13.044889, 11.166651, -28.060122, 12.979725, 10.923248, -27.034447, 13.723143, 10.398659, -27.435986, 13.376825, 11.196117, -28.256086, 11.096142, 7.324676, -28.004425, 12.171139, 8.214411, -28.3986, 11.137669, 7.350334, -28.004425, 12.171139, 8.214411, -28.256086, 11.096142, 7.324676, -27.958471, 11.59544, 7.810196, -27.958471, 11.59544, 7.810196, -28.256086, 11.096142, 7.324676, -27.841549, 11.343948, 7.662429, -28.004425, 12.171139, 8.214411, -27.223587, 12.345152, 8.655518, -27.574968, 13.101874, 9.166556, -28.004425, 12.171139, 8.214411, -27.335718, 12.067413, 8.358295, -27.223587, 12.345152, 8.655518, -26.456749, 12.367084, 8.993832, -27.034447, 13.723143, 10.398659, -27.574968, 13.101874, 9.166556, -27.034447, 13.723143, 10.398659, -26.456749, 12.367084, 8.993832, -26.222069, 12.594754, 9.523212, -27.034447, 13.723143, 10.398659, -26.222069, 12.594754, 9.523212, -26.492432, 13.021158, 9.833479, -26.435522, 13.525401, 10.803925, -26.549658, 13.852484, 11.521194, -27.034447, 13.723143, 10.398659, -26.549658, 13.852484, 11.521194, -26.435522, 13.525401, 10.803925, -25.835228, 13.148436, 10.873161, -26.549658, 13.852484, 11.521194, -25.835228, 13.148436, 10.873161, -25.778147, 13.206228, 11.136642, -27.034447, 13.723143, 10.398659, -28.157778, 12.77416, 10.008541, -27.574968, 13.101874, 9.166556, -27.610704, 13.283167, 10.320328, -28.157778, 12.77416, 10.008541, -27.034447, 13.723143, 10.398659, -28.157778, 12.77416, 10.008541, -27.610704, 13.283167, 10.320328, -28.219557, 12.675113, 10.536343, -28.157778, 12.77416, 10.008541, -28.219557, 12.675113, 10.536343, -28.328056, 12.602215, 10.297457, -27.034447, 13.723143, 10.398659, -28.175543, 12.86018, 10.661373, -27.610704, 13.283167, 10.320328, -27.034447, 13.723143, 10.398659, -28.060122, 12.979725, 10.923248, -28.175543, 12.86018, 10.661373, -26.492432, 13.021158, 9.833479, -26.016369, 12.854247, 9.998339, -27.034447, 13.723143, 10.398659, -26.492432, 13.021158, 9.833479, -26.116678, 12.750883, 9.77084, -26.016369, 12.854247, 9.998339, -26.549658, 13.852484, 11.521194, -27.435986, 13.376825, 11.196117, -27.034447, 13.723143, 10.398659, -27.435986, 13.376825, 11.196117, -26.549658, 13.852484, 11.521194, -27.393269, 13.267618, 11.867287, -27.435986, 13.376825, 11.196117, -27.393269, 13.267618, 11.867287, -27.637686, 13.179714, 11.523783, -26.435522, 13.525401, 10.803925, -26.016369, 12.854247, 9.998339, -25.865164, 12.972085, 10.685308, -26.435522, 13.525401, 10.803925, -27.034447, 13.723143, 10.398659, -26.016369, 12.854247, 9.998339, -26.09762, 13.550388, 12.580524, -26.750992, 13.556589, 11.991425, -26.549658, 13.852484, 11.521194, -26.750992, 13.556589, 11.991425, -26.09762, 13.550388, 12.580524, -26.272526, 13.433453, 12.685123, -26.750992, 13.556589, 11.991425, -26.272526, 13.433453, 12.685123, -26.766165, 13.395969, 12.328079, -27.223587, 12.345152, 8.655518, -26.456749, 12.367084, 8.993832, -27.574968, 13.101874, 9.166556, -27.223587, 12.345152, 8.655518, -26.759527, 12.152982, 8.67496, -26.456749, 12.367084, 8.993832, -27.335718, 12.067413, 8.358295, -26.946072, 11.938253, 8.469797, -27.223587, 12.345152, 8.655518, -27.335718, 12.067413, 8.358295, -27.053827, 11.894838, 8.359913, -26.946072, 11.938253, 8.469797, -27.335718, 12.067413, 8.358295, -28.004425, 12.171139, 8.214411, -27.266567, 11.75786, 8.137337, -27.958471, 11.59544, 7.810196, -27.266567, 11.75786, 8.137337, -28.004425, 12.171139, 8.214411, -27.958471, 11.59544, 7.810196, -27.63729, 11.513017, 7.833111, -27.266567, 11.75786, 8.137337, -25.299118, 14.016339, 11.705952, -26.202145, 13.431294, 12.758247, -26.33764, 13.542391, 12.539379, -25.299118, 14.016339, 11.705952, -24.55057, 13.487381, 12.600025, -26.202145, 13.431294, 12.758247, -25.729456, 13.096121, 10.875708, -25.798758, 13.544817, 11.6329155, -25.976883, 13.186634, 11.381079, -25.729456, 13.096121, 10.875708, -25.299118, 14.016339, 11.705952, -25.798758, 13.544817, 11.6329155, -20.600985, 11.212358, 8.53599, -21.567623, 12.368253, 9.249824, -22.19324, 11.752106, 8.746279, -20.600985, 11.212358, 8.53599, -20.480156, 11.286333, 8.646276, -21.567623, 12.368253, 9.249824, -21.567623, 12.368253, 9.249824, -22.58087, 11.89322, 8.845634, -22.19324, 11.752106, 8.746279, -21.567623, 12.368253, 9.249824, -22.426706, 12.42362, 9.235527, -22.58087, 11.89322, 8.845634, -22.426706, 12.42362, 9.235527, -21.567623, 12.368253, 9.249824, -22.728525, 13.355524, 9.92947, -22.426706, 12.42362, 9.235527, -22.728525, 13.355524, 9.92947, -23.621424, 12.259226, 9.112419, -22.426706, 12.42362, 9.235527, -23.621424, 12.259226, 9.112419, -22.929403, 12.018518, 8.935175, -23.621424, 12.259226, 9.112419, -23.71438, 12.69767, 9.51326, -23.778412, 12.302008, 9.216736, -23.621424, 12.259226, 9.112419, -22.728525, 13.355524, 9.92947, -23.71438, 12.69767, 9.51326, -24.753212, 12.792858, 9.863087, -25.008253, 13.342491, 10.636538, -25.004257, 12.857843, 10.124001, -24.753212, 12.792858, 9.863087, -24.116346, 13.970501, 10.84609, -25.008253, 13.342491, 10.636538, -20.411009, 11.269615, 8.813364, -21.567623, 12.368253, 9.249824, -20.480156, 11.286333, 8.646276, -21.567623, 12.368253, 9.249824, -20.411009, 11.269615, 8.813364, -20.987125, 11.792372, 9.251802, -20.987125, 11.792372, 9.251802, -20.411009, 11.269615, 8.813364, -20.750517, 11.554993, 9.368832, -21.567623, 12.368253, 9.249824, -21.917282, 12.636011, 10.289265, -22.728525, 13.355524, 9.92947, -21.567623, 12.368253, 9.249824, -21.553564, 12.347399, 10.101149, -21.917282, 12.636011, 10.289265, -22.131565, 12.746388, 11.290453, -24.116346, 13.970501, 10.84609, -22.728525, 13.355524, 9.92947, -24.116346, 13.970501, 10.84609, -22.131565, 12.746388, 11.290453, -22.724337, 12.956984, 11.684092, -24.116346, 13.970501, 10.84609, -22.724337, 12.956984, 11.684092, -23.211079, 13.356783, 11.415373, -24.388641, 13.79298, 11.697922, -25.299118, 14.016339, 11.705952, -24.116346, 13.970501, 10.84609, -25.299118, 14.016339, 11.705952, -24.388641, 13.79298, 11.697922, -24.252804, 13.45727, 12.470831, -25.299118, 14.016339, 11.705952, -24.252804, 13.45727, 12.470831, -24.55057, 13.487381, 12.600025, -24.116346, 13.970501, 10.84609, -23.71438, 12.69767, 9.51326, -22.728525, 13.355524, 9.92947, -24.050045, 13.353634, 10.197813, -23.71438, 12.69767, 9.51326, -24.116346, 13.970501, 10.84609, -23.71438, 12.69767, 9.51326, -24.050045, 13.353634, 10.197813, -24.313118, 12.454627, 9.572146, -23.71438, 12.69767, 9.51326, -24.313118, 12.454627, 9.572146, -24.046837, 12.398183, 9.39505, -24.116346, 13.970501, 10.84609, -24.459291, 12.676998, 9.6678705, -24.050045, 13.353634, 10.197813, -24.116346, 13.970501, 10.84609, -24.753212, 12.792858, 9.863087, -24.459291, 12.676998, 9.6678705, -23.211079, 13.356783, 11.415373, -23.25923, 13.221777, 12.038786, -24.116346, 13.970501, 10.84609, -23.211079, 13.356783, 11.415373, -23.004206, 13.122009, 11.869499, -23.25923, 13.221777, 12.038786, -25.299118, 14.016339, 11.705952, -25.008253, 13.342491, 10.636538, -24.116346, 13.970501, 10.84609, -25.008253, 13.342491, 10.636538, -25.299118, 14.016339, 11.705952, -25.729456, 13.096121, 10.875708, -25.008253, 13.342491, 10.636538, -25.729456, 13.096121, 10.875708, -25.374859, 13.007589, 10.507263, -24.388641, 13.79298, 11.697922, -23.25923, 13.221777, 12.038786, -24.033789, 13.251799, 12.374115, -24.388641, 13.79298, 11.697922, -24.116346, 13.970501, 10.84609, -23.25923, 13.221777, 12.038786, -26.33764, 13.542391, 12.539379, -25.798758, 13.544817, 11.6329155, -25.299118, 14.016339, 11.705952, -25.798758, 13.544817, 11.6329155, -26.33764, 13.542391, 12.539379, -26.4637, 13.351927, 12.376793, -25.798758, 13.544817, 11.6329155, -26.4637, 13.351927, 12.376793, -26.145252, 13.288563, 11.72068, -21.917282, 12.636011, 10.289265, -22.131565, 12.746388, 11.290453, -22.728525, 13.355524, 9.92947, -21.917282, 12.636011, 10.289265, -21.7968, 12.497051, 10.865448, -22.131565, 12.746388, 11.290453, -21.553564, 12.347399, 10.101149, -21.57792, 12.244032, 10.592348, -21.917282, 12.636011, 10.289265, -21.553564, 12.347399, 10.101149, -21.464146, 12.196934, 10.445953, -21.57792, 12.244032, 10.592348, -21.553564, 12.347399, 10.101149, -21.567623, 12.368253, 9.249824, -21.231346, 12.042698, 10.149359, -20.987125, 11.792372, 9.251802, -21.231346, 12.042698, 10.149359, -21.567623, 12.368253, 9.249824, -20.987125, 11.792372, 9.251802, -20.92406, 11.751565, 9.648912, -21.231346, 12.042698, 10.149359, -25.705019, 13.809881, 13.562584, -26.45702, 13.504189, 12.279745, -26.186665, 13.570251, 12.245989, -25.705019, 13.809881, 13.562584, -26.888538, 13.45844, 13.87192, -26.45702, 13.504189, 12.279745, -24.945316, 12.747828, 13.355633, -25.55226, 13.354175, 13.0715885, -25.309752, 12.956354, 12.9552, -24.945316, 12.747828, 13.355633, -25.705019, 13.809881, 13.562584, -25.55226, 13.354175, 13.0715885, -24.723381, 10.182662, 18.724218, -24.892126, 11.507984, 17.707823, -24.315966, 10.822086, 17.237934, -24.723381, 10.182662, 18.724218, -24.853382, 10.273891, 18.805033, -24.892126, 11.507984, 17.707823, -24.892126, 11.507984, 17.707823, -24.260817, 10.9978075, 16.859945, -24.315966, 10.822086, 17.237934, -24.892126, 11.507984, 17.707823, -24.594227, 11.594991, 16.92428, -24.260817, 10.9978075, 16.859945, -24.594227, 11.594991, 16.92428, -24.892126, 11.507984, 17.707823, -24.99707, 12.669626, 16.504105, -24.594227, 11.594991, 16.92428, -24.99707, 12.669626, 16.504105, -24.11179, 11.456928, 15.843534, -24.594227, 11.594991, 16.92428, -24.11179, 11.456928, 15.843534, -24.211235, 11.154295, 16.519825, -24.11179, 11.456928, 15.843534, -24.396267, 11.977171, 15.664667, -24.146303, 11.527758, 15.66567, -24.11179, 11.456928, 15.843534, -24.99707, 12.669626, 16.504105, -24.396267, 11.977171, 15.664667, -24.372864, 12.190012, 14.588343, -24.92865, 12.906826, 14.1381035, -24.519852, 12.320793, 14.269573, -24.372864, 12.190012, 14.588343, -25.306261, 13.528545, 14.961657, -24.92865, 12.906826, 14.1381035, -25.035671, 10.291328, 18.806995, -24.892126, 11.507984, 17.707823, -24.853382, 10.273891, 18.805033, -24.892126, 11.507984, 17.707823, -25.035671, 10.291328, 18.806995, -25.174652, 10.921768, 18.176992, -25.174652, 10.921768, 18.176992, -25.035671, 10.291328, 18.806995, -25.392036, 10.70524, 18.326195, -24.892126, 11.507984, 17.707823, -25.717178, 12.011866, 17.041342, -24.99707, 12.669626, 16.504105, -24.892126, 11.507984, 17.707823, -25.703766, 11.673546, 17.411934, -25.717178, 12.011866, 17.041342, -26.566265, 12.347487, 16.495499, -25.306261, 13.528545, 14.961657, -24.99707, 12.669626, 16.504105, -25.306261, 13.528545, 14.961657, -26.566265, 12.347487, 16.495499, -26.699371, 12.663515, 15.829714, -25.306261, 13.528545, 14.961657, -26.699371, 12.663515, 15.829714, -26.24021, 13.016304, 15.522837, -26.053259, 13.553265, 14.382238, -25.705019, 13.809881, 13.562584, -25.306261, 13.528545, 14.961657, -25.705019, 13.809881, 13.562584, -26.053259, 13.553265, 14.382238, -26.875654, 13.388691, 14.190426, -25.705019, 13.809881, 13.562584, -26.875654, 13.388691, 14.190426, -26.888538, 13.45844, 13.87192, -25.306261, 13.528545, 14.961657, -24.396267, 11.977171, 15.664667, -24.99707, 12.669626, 16.504105, -24.82944, 12.781982, 15.184017, -24.396267, 11.977171, 15.664667, -25.306261, 13.528545, 14.961657, -24.396267, 11.977171, 15.664667, -24.82944, 12.781982, 15.184017, -24.26432, 11.775922, 15.060766, -24.396267, 11.977171, 15.664667, -24.26432, 11.775922, 15.060766, -24.206535, 11.67166, 15.364321, -25.306261, 13.528545, 14.961657, -24.306345, 12.02211, 14.9169445, -24.82944, 12.781982, 15.184017, -25.306261, 13.528545, 14.961657, -24.372864, 12.190012, 14.588343, -24.306345, 12.02211, 14.9169445, -26.24021, 13.016304, 15.522837, -26.82347, 13.022417, 15.237913, -25.306261, 13.528545, 14.961657, -26.24021, 13.016304, 15.522837, -26.765692, 12.877438, 15.523392, -26.82347, 13.022417, 15.237913, -25.705019, 13.809881, 13.562584, -24.92865, 12.906826, 14.1381035, -25.306261, 13.528545, 14.961657, -24.92865, 12.906826, 14.1381035, -25.705019, 13.809881, 13.562584, -24.945316, 12.747828, 13.355633, -24.92865, 12.906826, 14.1381035, -24.945316, 12.747828, 13.355633, -24.738127, 12.5664425, 13.806084, -26.053259, 13.553265, 14.382238, -26.82347, 13.022417, 15.237913, -26.853886, 13.155892, 14.403858, -26.053259, 13.553265, 14.382238, -25.306261, 13.528545, 14.961657, -26.82347, 13.022417, 15.237913, -26.186665, 13.570251, 12.245989, -25.55226, 13.354175, 13.0715885, -25.705019, 13.809881, 13.562584, -25.55226, 13.354175, 13.0715885, -26.186665, 13.570251, 12.245989, -26.027418, 13.354084, 12.165661, -25.55226, 13.354175, 13.0715885, -26.027418, 13.354084, 12.165661, -25.556536, 13.13711, 12.689047, -25.717178, 12.011866, 17.041342, -26.566265, 12.347487, 16.495499, -24.99707, 12.669626, 16.504105, -25.717178, 12.011866, 17.041342, -26.317577, 11.997127, 16.929232, -26.566265, 12.347487, 16.495499, -25.703766, 11.673546, 17.411934, -26.153154, 11.680018, 17.200329, -25.717178, 12.011866, 17.041342, -25.703766, 11.673546, 17.411934, -26.069336, 11.597685, 17.352945, -26.153154, 11.680018, 17.200329, -25.703766, 11.673546, 17.411934, -24.892126, 11.507984, 17.707823, -25.896534, 11.372709, 17.657299, -25.174652, 10.921768, 18.176992, -25.896534, 11.372709, 17.657299, -24.892126, 11.507984, 17.707823, -25.174652, 10.921768, 18.176992, -25.57406, 10.966168, 18.087864, -25.896534, 11.372709, 17.657299, -23.578579, 3.6379063, 11.557745, -24.12619, 7.057568, 11.959915, -23.8756, 7.125276, 11.658489, -23.578579, 3.6379063, 11.557745, -23.888453, 3.5751674, 11.919102, -24.12619, 7.057568, 11.959915, -23.836985, -0.21907139, 11.791069, -23.888453, 3.5751674, 11.919102, -23.578579, 3.6379063, 11.557745, -23.836985, -0.21907139, 11.791069, -24.181253, -0.21472502, 12.204487, -23.888453, 3.5751674, 11.919102, -24.709448, 3.4888213, 11.70261, -24.880796, 6.9884076, 11.3729, -24.819984, 6.963768, 11.765211, -24.709448, 3.4888213, 11.70261, -24.77573, 3.5122192, 11.244535, -24.880796, 6.9884076, 11.3729, -24.003609, 3.6379066, 10.807255, -23.93851, 7.1495028, 11.266003, -24.242756, 7.125276, 11.010187, -24.003609, 3.6379066, 10.807255, -23.637373, 3.6615102, 11.095457, -23.93851, 7.1495028, 11.266003, -24.819984, 6.963768, 11.765211, -25.485994, 10.232269, 11.85763, -25.442476, 10.210943, 12.117752, -24.819984, 6.963768, 11.765211, -24.880796, 6.9884076, 11.3729, -25.485994, 10.232269, 11.85763, -23.8756, 7.125276, 11.658489, -24.984426, 10.2888, 12.251429, -24.815989, 10.342636, 12.050236, -23.8756, 7.125276, 11.658489, -24.12619, 7.057568, 11.959915, -24.984426, 10.2888, 12.251429, -24.815989, 10.342636, 12.050236, -24.984426, 10.2888, 12.251429, -25.548643, 11.995004, 12.470713, -24.815989, 10.342636, 12.050236, -25.548643, 11.995004, 12.470713, -25.430141, 12.056503, 12.32415, -23.888453, 3.5751674, 11.919102, -24.514824, 6.9884076, 12.019112, -24.12619, 7.057568, 11.959915, -23.888453, 3.5751674, 11.919102, -24.350702, 3.512219, 11.995026, -24.514824, 6.9884076, 12.019112, -24.12619, 7.057568, 11.959915, -24.514824, 6.9884076, 12.019112, -25.241776, 10.232269, 12.288855, -24.12619, 7.057568, 11.959915, -25.241776, 10.232269, 12.288855, -24.984426, 10.2888, 12.251429, -24.77573, 3.5122192, 11.244535, -24.63014, 7.057568, 11.070068, -24.880796, 6.9884076, 11.3729, -24.77573, 3.5122192, 11.244535, -24.472868, 3.5751677, 10.8871765, -24.63014, 7.057568, 11.070068, -24.880796, 6.9884076, 11.3729, -24.63014, 7.057568, 11.070068, -25.321539, 10.2888, 11.656172, -24.880796, 6.9884076, 11.3729, -25.321539, 10.2888, 11.656172, -25.485994, 10.232269, 11.85763, -24.242756, 7.125276, 11.010187, -24.857891, 10.362101, 11.78668, -25.062378, 10.342636, 11.615177, -24.242756, 7.125276, 11.010187, -23.93851, 7.1495028, 11.266003, -24.857891, 10.362101, 11.78668, -23.637373, 3.6615102, 11.095457, -23.8756, 7.125276, 11.658489, -23.93851, 7.1495028, 11.266003, -23.637373, 3.6615102, 11.095457, -23.578579, 3.6379063, 11.557745, -23.8756, 7.125276, 11.658489, -23.93851, 7.1495028, 11.266003, -24.815989, 10.342636, 12.050236, -24.857891, 10.362101, 11.78668, -23.93851, 7.1495028, 11.266003, -23.8756, 7.125276, 11.658489, -24.815989, 10.342636, 12.050236, -24.472868, 3.5751677, 10.8871765, -24.242756, 7.125276, 11.010187, -24.63014, 7.057568, 11.070068, -24.472868, 3.5751677, 10.8871765, -24.003609, 3.6379066, 10.807255, -24.242756, 7.125276, 11.010187, -24.868216, -0.21472502, 10.9914875, -24.003609, 3.6379066, 10.807255, -24.472868, 3.5751677, 10.8871765, -24.868216, -0.21472502, 10.9914875, -24.336596, -0.21907139, 10.908884, -24.003609, 3.6379066, 10.807255, -24.63014, 7.057568, 11.070068, -25.062378, 10.342636, 11.615177, -25.321539, 10.2888, 11.656172, -24.63014, 7.057568, 11.070068, -24.242756, 7.125276, 11.010187, -25.062378, 10.342636, 11.615177, -25.321539, 10.2888, 11.656172, -25.062378, 10.342636, 11.615177, -25.613142, 12.056503, 12.001019, -25.321539, 10.2888, 11.656172, -25.613142, 12.056503, 12.001019, -25.79978, 11.995004, 12.027269, -25.883152, 11.911559, 12.367321, -26.206875, 13.210678, 12.70623, -25.735323, 11.93475, 12.496033, -25.883152, 11.911559, 12.367321, -26.31439, 13.191008, 12.611546, -26.206875, 13.210678, 12.70623, -25.548643, 11.995004, 12.470713, -25.993643, 13.325804, 12.585468, -25.430141, 12.056503, 12.32415, -25.548643, 11.995004, 12.470713, -26.075993, 13.270106, 12.690508, -25.993643, 13.325804, 12.585468, -25.735323, 11.93475, 12.496033, -26.075993, 13.270106, 12.690508, -25.548643, 11.995004, 12.470713, -25.735323, 11.93475, 12.496033, -26.206875, 13.210678, 12.70623, -26.075993, 13.270106, 12.690508, -25.463963, 12.079072, 12.129919, -26.127062, 13.325804, 12.349884, -25.613142, 12.056503, 12.001019, -25.463963, 12.079072, 12.129919, -26.021097, 13.349201, 12.445444, -26.127062, 13.325804, 12.349884, -25.430141, 12.056503, 12.32415, -26.021097, 13.349201, 12.445444, -25.463963, 12.079072, 12.129919, -25.430141, 12.056503, 12.32415, -25.993643, 13.325804, 12.585468, -26.021097, 13.349201, 12.445444, -25.79978, 11.995004, 12.027269, -26.340294, 13.210678, 12.470644, -25.917505, 11.93475, 12.174345, -25.79978, 11.995004, 12.027269, -26.259499, 13.270106, 12.366484, -26.340294, 13.210678, 12.470644, -25.613142, 12.056503, 12.001019, -26.259499, 13.270106, 12.366484, -25.79978, 11.995004, 12.027269, -25.613142, 12.056503, 12.001019, -26.127062, 13.325804, 12.349884, -26.259499, 13.270106, 12.366484, -24.715683, -0.21037531, 12.288706, -24.709448, 3.4888213, 11.70261, -24.350702, 3.512219, 11.995026, -24.715683, -0.21037531, 12.288706, -25.129576, -0.2087202, 11.940542, -24.709448, 3.4888213, 11.70261, -25.917505, 11.93475, 12.174345, -26.31439, 13.191008, 12.611546, -25.883152, 11.911559, 12.367321, -25.917505, 11.93475, 12.174345, -26.340294, 13.210678, 12.470644, -26.31439, 13.191008, 12.611546, -24.350702, 3.512219, 11.995026, -24.819984, 6.963768, 11.765211, -24.514824, 6.9884076, 12.019112, -24.350702, 3.512219, 11.995026, -24.709448, 3.4888213, 11.70261, -24.819984, 6.963768, 11.765211, -25.129576, -0.2087202, 11.940542, -24.77573, 3.5122192, 11.244535, -24.709448, 3.4888213, 11.70261, -25.129576, -0.2087202, 11.940542, -25.215294, -0.21037531, 11.406523, -24.77573, 3.5122192, 11.244535, -24.336596, -0.21907139, 10.908884, -23.637373, 3.6615102, 11.095457, -24.003609, 3.6379066, 10.807255, -24.336596, -0.21907139, 10.908884, -23.924746, -0.2207303, 11.258205, -23.637373, 3.6615102, 11.095457, -24.181253, -0.21472502, 12.204487, -24.350702, 3.512219, 11.995026, -23.888453, 3.5751674, 11.919102, -24.181253, -0.21472502, 12.204487, -24.715683, -0.21037531, 12.288706, -24.350702, 3.512219, 11.995026, -25.215294, -0.21037531, 11.406523, -24.472868, 3.5751677, 10.8871765, -24.77573, 3.5122192, 11.244535, -25.215294, -0.21037531, 11.406523, -24.868216, -0.21472502, 10.9914875, -24.472868, 3.5751677, 10.8871765, -23.924746, -0.2207303, 11.258205, -23.578579, 3.6379063, 11.557745, -23.637373, 3.6615102, 11.095457, -23.924746, -0.2207303, 11.258205, -23.836985, -0.21907139, 11.791069, -23.578579, 3.6379063, 11.557745, -24.514824, 6.9884076, 12.019112, -25.442476, 10.210943, 12.117752, -25.241776, 10.232269, 12.288855, -24.514824, 6.9884076, 12.019112, -24.819984, 6.963768, 11.765211, -25.442476, 10.210943, 12.117752, -25.241776, 10.232269, 12.288855, -25.883152, 11.911559, 12.367321, -25.735323, 11.93475, 12.496033, -25.241776, 10.232269, 12.288855, -25.442476, 10.210943, 12.117752, -25.883152, 11.911559, 12.367321, -25.442476, 10.210943, 12.117752, -25.917505, 11.93475, 12.174345, -25.883152, 11.911559, 12.367321, -25.442476, 10.210943, 12.117752, -25.485994, 10.232269, 11.85763, -25.917505, 11.93475, 12.174345, -24.984426, 10.2888, 12.251429, -25.735323, 11.93475, 12.496033, -25.548643, 11.995004, 12.470713, -24.984426, 10.2888, 12.251429, -25.241776, 10.232269, 12.288855, -25.735323, 11.93475, 12.496033, -25.062378, 10.342636, 11.615177, -25.463963, 12.079072, 12.129919, -25.613142, 12.056503, 12.001019, -25.062378, 10.342636, 11.615177, -24.857891, 10.362101, 11.78668, -25.463963, 12.079072, 12.129919, -24.857891, 10.362101, 11.78668, -25.430141, 12.056503, 12.32415, -25.463963, 12.079072, 12.129919, -24.857891, 10.362101, 11.78668, -24.815989, 10.342636, 12.050236, -25.430141, 12.056503, 12.32415, -25.485994, 10.232269, 11.85763, -25.79978, 11.995004, 12.027269, -25.917505, 11.93475, 12.174345, -25.485994, 10.232269, 11.85763, -25.321539, 10.2888, 11.656172, -25.79978, 11.995004, 12.027269, 14.960796, 7.3177958, 10.284792, 14.197514, 7.0888658, 10.165964, 14.230383, 7.219601, 10.105017, 14.960796, 7.3177958, 10.284792, 14.899889, 6.6811314, 10.556076, 14.197514, 7.0888658, 10.165964, 15.035656, 7.2923393, 9.619492, 14.762205, 7.297971, 9.966335, 14.763325, 7.2744055, 9.734959, 15.035656, 7.2923393, 9.619492, 14.960796, 7.3177958, 10.284792, 14.762205, 7.297971, 9.966335, 17.810453, 5.5428567, 9.76613, 17.232166, 6.147483, 10.085447, 17.132032, 6.2065306, 9.564142, 17.810453, 5.5428567, 9.76613, 17.822802, 5.5103865, 9.856148, 17.232166, 6.147483, 10.085447, 17.232166, 6.147483, 10.085447, 16.95009, 6.3580484, 9.530783, 17.132032, 6.2065306, 9.564142, 17.232166, 6.147483, 10.085447, 16.89884, 6.4360313, 9.857285, 16.95009, 6.3580484, 9.530783, 16.89884, 6.4360313, 9.857285, 17.232166, 6.147483, 10.085447, 16.58012, 6.736306, 10.276844, 16.89884, 6.4360313, 9.857285, 16.58012, 6.736306, 10.276844, 16.462008, 6.7533646, 9.4380245, 16.89884, 6.4360313, 9.857285, 16.462008, 6.7533646, 9.4380245, 16.786558, 6.492572, 9.500252, 16.462008, 6.7533646, 9.4380245, 16.302326, 6.86697, 9.66189, 16.365639, 6.7913795, 9.442841, 16.462008, 6.7533646, 9.4380245, 16.58012, 6.736306, 10.276844, 16.302326, 6.86697, 9.66189, 15.768839, 7.134306, 9.500018, 15.418615, 7.241414, 9.825178, 15.579935, 7.1703434, 9.529699, 15.768839, 7.134306, 9.500018, 15.7336235, 7.169642, 10.347719, 15.418615, 7.241414, 9.825178, 17.788603, 5.4442396, 9.916714, 17.232166, 6.147483, 10.085447, 17.822802, 5.5103865, 9.856148, 17.232166, 6.147483, 10.085447, 17.788603, 5.4442396, 9.916714, 17.429657, 5.7386765, 10.057089, 17.429657, 5.7386765, 10.057089, 17.788603, 5.4442396, 9.916714, 17.469614, 5.541645, 10.084452, 17.232166, 6.147483, 10.085447, 16.729776, 6.127803, 10.365282, 16.58012, 6.736306, 10.276844, 17.232166, 6.147483, 10.085447, 16.926506, 5.9454803, 10.319639, 16.729776, 6.127803, 10.365282, 16.288668, 6.0046787, 10.627327, 15.7336235, 7.169642, 10.347719, 16.58012, 6.736306, 10.276844, 15.7336235, 7.169642, 10.347719, 16.288668, 6.0046787, 10.627327, 15.925083, 6.171483, 10.651344, 15.7336235, 7.169642, 10.347719, 15.925083, 6.171483, 10.651344, 15.8492565, 6.533848, 10.554647, 15.306034, 6.9726505, 10.442774, 14.960796, 7.3177958, 10.284792, 15.7336235, 7.169642, 10.347719, 14.960796, 7.3177958, 10.284792, 15.306034, 6.9726505, 10.442774, 15.061283, 6.615505, 10.587776, 14.960796, 7.3177958, 10.284792, 15.061283, 6.615505, 10.587776, 14.899889, 6.6811314, 10.556076, 15.7336235, 7.169642, 10.347719, 16.302326, 6.86697, 9.66189, 16.58012, 6.736306, 10.276844, 15.95689, 7.068595, 9.972179, 16.302326, 6.86697, 9.66189, 15.7336235, 7.169642, 10.347719, 16.302326, 6.86697, 9.66189, 15.95689, 7.068595, 9.972179, 16.037476, 6.9235826, 9.459768, 16.302326, 6.86697, 9.66189, 16.037476, 6.9235826, 9.459768, 16.200966, 6.8668146, 9.453747, 15.7336235, 7.169642, 10.347719, 15.948803, 7.0493326, 9.487534, 15.95689, 7.068595, 9.972179, 15.7336235, 7.169642, 10.347719, 15.768839, 7.134306, 9.500018, 15.948803, 7.0493326, 9.487534, 15.8492565, 6.533848, 10.554647, 15.598484, 6.34639, 10.67927, 15.7336235, 7.169642, 10.347719, 15.8492565, 6.533848, 10.554647, 15.754543, 6.2731757, 10.668556, 15.598484, 6.34639, 10.67927, 14.960796, 7.3177958, 10.284792, 15.418615, 7.241414, 9.825178, 15.7336235, 7.169642, 10.347719, 15.418615, 7.241414, 9.825178, 14.960796, 7.3177958, 10.284792, 15.035656, 7.2923393, 9.619492, 15.418615, 7.241414, 9.825178, 15.035656, 7.2923393, 9.619492, 15.302166, 7.2475796, 9.57913, 15.306034, 6.9726505, 10.442774, 15.598484, 6.34639, 10.67927, 15.17808, 6.500619, 10.592104, 15.306034, 6.9726505, 10.442774, 15.7336235, 7.169642, 10.347719, 15.598484, 6.34639, 10.67927, 14.230383, 7.219601, 10.105017, 14.762205, 7.297971, 9.966335, 14.960796, 7.3177958, 10.284792, 14.762205, 7.297971, 9.966335, 14.230383, 7.219601, 10.105017, 14.228041, 7.218266, 9.957092, 14.762205, 7.297971, 9.966335, 14.228041, 7.218266, 9.957092, 14.580687, 7.2687683, 9.813696, 16.729776, 6.127803, 10.365282, 16.288668, 6.0046787, 10.627327, 16.58012, 6.736306, 10.276844, 16.729776, 6.127803, 10.365282, 16.561163, 5.9042377, 10.517782, 16.288668, 6.0046787, 10.627327, 16.926506, 5.9454803, 10.319639, 16.737123, 5.796604, 10.437109, 16.729776, 6.127803, 10.365282, 16.926506, 5.9454803, 10.319639, 16.830635, 5.779464, 10.403534, 16.737123, 5.796604, 10.437109, 16.926506, 5.9454803, 10.319639, 17.232166, 6.147483, 10.085447, 17.020973, 5.7145977, 10.328281, 17.429657, 5.7386765, 10.057089, 17.020973, 5.7145977, 10.328281, 17.232166, 6.147483, 10.085447, 17.429657, 5.7386765, 10.057089, 17.308277, 5.615484, 10.174715, 17.020973, 5.7145977, 10.328281, 13.748751, 7.053542, 10.572222, 14.344303, 7.2258663, 9.903076, 14.400131, 7.268545, 10.054003, 13.748751, 7.053542, 10.572222, 13.42976, 6.790598, 9.931854, 14.344303, 7.2258663, 9.903076, 14.244236, 6.5294213, 10.857023, 14.149033, 6.936182, 10.532454, 14.338385, 6.748352, 10.605932, 14.244236, 6.5294213, 10.857023, 13.748751, 7.053542, 10.572222, 14.149033, 6.936182, 10.532454, 11.830196, 3.7078786, 11.580435, 12.08853, 4.698938, 11.459175, 12.606657, 4.4480114, 11.634403, 11.830196, 3.7078786, 11.580435, 11.739759, 3.7365186, 11.530159, 12.08853, 4.698938, 11.459175, 12.08853, 4.698938, 11.459175, 12.792095, 4.643725, 11.622538, 12.606657, 4.4480114, 11.634403, 12.08853, 4.698938, 11.459175, 12.569534, 4.9534965, 11.509512, 12.792095, 4.643725, 11.622538, 12.569534, 4.9534965, 11.509512, 12.08853, 4.698938, 11.459175, 12.50172, 5.6512947, 11.328012, 12.569534, 4.9534965, 11.509512, 12.50172, 5.6512947, 11.328012, 13.291006, 5.162043, 11.5888405, 12.569534, 4.9534965, 11.509512, 13.291006, 5.162043, 11.5888405, 12.958977, 4.8188252, 11.61166, 13.291006, 5.162043, 11.5888405, 13.2354355, 5.49573, 11.458206, 13.363539, 5.246913, 11.546379, 13.291006, 5.162043, 11.5888405, 12.50172, 5.6512947, 11.328012, 13.2354355, 5.49573, 11.458206, 13.796944, 5.897662, 11.320793, 13.790836, 6.4103017, 11.012831, 13.913025, 6.053091, 11.199114, 13.796944, 5.897662, 11.320793, 13.121664, 6.529729, 10.9958515, 13.790836, 6.4103017, 11.012831, 11.699358, 3.746626, 11.430775, 12.08853, 4.698938, 11.459175, 11.739759, 3.7365186, 11.530159, 12.08853, 4.698938, 11.459175, 11.699358, 3.746626, 11.430775, 11.891642, 4.2572117, 11.316582, 11.891642, 4.2572117, 11.316582, 11.699358, 3.746626, 11.430775, 11.803057, 4.0996614, 11.194469, 12.08853, 4.698938, 11.459175, 12.198441, 5.1560144, 10.946365, 12.50172, 5.6512947, 11.328012, 12.08853, 4.698938, 11.459175, 12.062822, 4.8732367, 10.978052, 12.198441, 5.1560144, 10.946365, 12.262251, 5.4878073, 10.418972, 13.121664, 6.529729, 10.9958515, 12.50172, 5.6512947, 11.328012, 13.121664, 6.529729, 10.9958515, 12.262251, 5.4878073, 10.418972, 12.532429, 5.8353195, 10.266994, 13.121664, 6.529729, 10.9958515, 12.532429, 5.8353195, 10.266994, 12.735952, 6.1089916, 10.515257, 13.298405, 6.7030754, 10.491749, 13.748751, 7.053542, 10.572222, 13.121664, 6.529729, 10.9958515, 13.748751, 7.053542, 10.572222, 13.298405, 6.7030754, 10.491749, 13.275257, 6.6692533, 9.985384, 13.748751, 7.053542, 10.572222, 13.275257, 6.6692533, 9.985384, 13.42976, 6.790598, 9.931854, 13.121664, 6.529729, 10.9958515, 13.2354355, 5.49573, 11.458206, 12.50172, 5.6512947, 11.328012, 13.256226, 6.064356, 11.224297, 13.2354355, 5.49573, 11.458206, 13.121664, 6.529729, 10.9958515, 13.2354355, 5.49573, 11.458206, 13.256226, 6.064356, 11.224297, 13.609962, 5.5398574, 11.403032, 13.2354355, 5.49573, 11.458206, 13.609962, 5.5398574, 11.403032, 13.485888, 5.4047327, 11.477678, 13.121664, 6.529729, 10.9958515, 13.663889, 5.719082, 11.394192, 13.256226, 6.064356, 11.224297, 13.121664, 6.529729, 10.9958515, 13.796944, 5.897662, 11.320793, 13.663889, 5.719082, 11.394192, 12.735952, 6.1089916, 10.515257, 12.770527, 6.18994, 10.142353, 13.121664, 6.529729, 10.9958515, 12.735952, 6.1089916, 10.515257, 12.654997, 6.03539, 10.206213, 12.770527, 6.18994, 10.142353, 13.748751, 7.053542, 10.572222, 13.790836, 6.4103017, 11.012831, 13.121664, 6.529729, 10.9958515, 13.790836, 6.4103017, 11.012831, 13.748751, 7.053542, 10.572222, 14.244236, 6.5294213, 10.857023, 13.790836, 6.4103017, 11.012831, 14.244236, 6.5294213, 10.857023, 14.080233, 6.311716, 11.029364, 13.298405, 6.7030754, 10.491749, 12.770527, 6.18994, 10.142353, 13.176046, 6.4788733, 9.995405, 13.298405, 6.7030754, 10.491749, 13.121664, 6.529729, 10.9958515, 12.770527, 6.18994, 10.142353, 14.400131, 7.268545, 10.054003, 14.149033, 6.936182, 10.532454, 13.748751, 7.053542, 10.572222, 14.149033, 6.936182, 10.532454, 14.400131, 7.268545, 10.054003, 14.524361, 7.1721907, 10.108967, 14.149033, 6.936182, 10.532454, 14.524361, 7.1721907, 10.108967, 14.399626, 6.918585, 10.443947, 12.198441, 5.1560144, 10.946365, 12.262251, 5.4878073, 10.418972, 12.50172, 5.6512947, 11.328012, 12.198441, 5.1560144, 10.946365, 12.143154, 5.1808786, 10.591608, 12.262251, 5.4878073, 10.418972, 12.062822, 4.8732367, 10.978052, 12.071648, 4.931636, 10.687195, 12.198441, 5.1560144, 10.946365, 12.062822, 4.8732367, 10.978052, 12.028627, 4.8476563, 10.753109, 12.071648, 4.931636, 10.687195, 12.062822, 4.8732367, 10.978052, 12.08853, 4.698938, 11.459175, 11.94441, 4.6445174, 10.876879, 11.891642, 4.2572117, 11.316582, 11.94441, 4.6445174, 10.876879, 12.08853, 4.698938, 11.459175, 11.891642, 4.2572117, 11.316582, 11.852747, 4.307365, 11.084038, 11.94441, 4.6445174, 10.876879, 13.7075405, 7.556304, 10.0746155, 14.456242, 7.1163726, 9.9533615, 14.450766, 7.1982546, 10.098274, 13.7075405, 7.556304, 10.0746155, 13.637535, 7.179911, 9.4115, 14.456242, 7.1163726, 9.9533615, 13.580511, 7.079011, 10.661566, 13.885704, 7.279489, 10.325115, 13.870678, 7.088043, 10.517448, 13.580511, 7.079011, 10.661566, 13.7075405, 7.556304, 10.0746155, 13.885704, 7.279489, 10.325115, 10.434713, 6.2257376, 9.758102, 11.151068, 6.834375, 9.774982, 11.238207, 6.5067883, 10.272303, 10.434713, 6.2257376, 9.758102, 10.420666, 6.2614727, 9.657367, 11.151068, 6.834375, 9.774982, 11.151068, 6.834375, 9.774982, 11.449902, 6.5769014, 10.375746, 11.238207, 6.5067883, 10.272303, 11.151068, 6.834375, 9.774982, 11.530296, 6.857931, 10.121874, 11.449902, 6.5769014, 10.375746, 11.530296, 6.857931, 10.121874, 11.151068, 6.834375, 9.774982, 11.933365, 7.3444357, 9.894124, 11.530296, 6.857931, 10.121874, 11.933365, 7.3444357, 9.894124, 12.01959, 6.757763, 10.651842, 11.530296, 6.857931, 10.121874, 12.01959, 6.757763, 10.651842, 11.640572, 6.6390653, 10.468557, 12.01959, 6.757763, 10.651842, 12.206385, 6.9807854, 10.499392, 12.129288, 6.772664, 10.661032, 12.01959, 6.757763, 10.651842, 11.933365, 7.3444357, 9.894124, 12.206385, 6.9807854, 10.499392, 12.789576, 6.995577, 10.750685, 13.181564, 7.251497, 10.484621, 12.994299, 7.0094805, 10.72579, 12.789576, 6.995577, 10.750685, 12.882765, 7.613677, 10.007544, 13.181564, 7.251497, 10.484621, 10.444181, 6.238669, 9.556094, 11.151068, 6.834375, 9.774982, 10.420666, 6.2614727, 9.657367, 11.151068, 6.834375, 9.774982, 10.444181, 6.238669, 9.556094, 10.868927, 6.502743, 9.566353, 10.868927, 6.502743, 9.566353, 10.444181, 6.238669, 9.556094, 10.796675, 6.356696, 9.423105, 11.151068, 6.834375, 9.774982, 11.658583, 6.90143, 9.442226, 11.933365, 7.3444357, 9.894124, 11.151068, 6.834375, 9.774982, 11.422882, 6.7517605, 9.392457, 11.658583, 6.90143, 9.442226, 12.092066, 6.8811474, 9.071436, 12.882765, 7.613677, 10.007544, 11.933365, 7.3444357, 9.894124, 12.882765, 7.613677, 10.007544, 12.092066, 6.8811474, 9.071436, 12.501753, 6.965708, 9.113264, 12.882765, 7.613677, 10.007544, 12.501753, 6.965708, 9.113264, 12.637543, 7.2165165, 9.423414, 13.274715, 7.4356117, 9.746176, 13.7075405, 7.556304, 10.0746155, 12.882765, 7.613677, 10.007544, 13.7075405, 7.556304, 10.0746155, 13.274715, 7.4356117, 9.746176, 13.45668, 7.17616, 9.358056, 13.7075405, 7.556304, 10.0746155, 13.45668, 7.17616, 9.358056, 13.637535, 7.179911, 9.4115, 12.882765, 7.613677, 10.007544, 12.206385, 6.9807854, 10.499392, 11.933365, 7.3444357, 9.894124, 12.606947, 7.305357, 10.3043785, 12.206385, 6.9807854, 10.499392, 12.882765, 7.613677, 10.007544, 12.206385, 6.9807854, 10.499392, 12.606947, 7.305357, 10.3043785, 12.502178, 6.827238, 10.693289, 12.206385, 6.9807854, 10.499392, 12.502178, 6.827238, 10.693289, 12.314716, 6.8113565, 10.680117, 12.882765, 7.613677, 10.007544, 12.587612, 6.947036, 10.728325, 12.606947, 7.305357, 10.3043785, 12.882765, 7.613677, 10.007544, 12.789576, 6.995577, 10.750685, 12.587612, 6.947036, 10.728325, 12.637543, 7.2165165, 9.423414, 12.864445, 7.0854006, 9.161785, 12.882765, 7.613677, 10.007544, 12.637543, 7.2165165, 9.423414, 12.688946, 7.04373, 9.142518, 12.864445, 7.0854006, 9.161785, 13.7075405, 7.556304, 10.0746155, 13.181564, 7.251497, 10.484621, 12.882765, 7.613677, 10.007544, 13.181564, 7.251497, 10.484621, 13.7075405, 7.556304, 10.0746155, 13.580511, 7.079011, 10.661566, 13.181564, 7.251497, 10.484621, 13.580511, 7.079011, 10.661566, 13.291115, 7.061289, 10.697266, 13.274715, 7.4356117, 9.746176, 12.864445, 7.0854006, 9.161785, 13.339142, 7.0672264, 9.294153, 13.274715, 7.4356117, 9.746176, 12.882765, 7.613677, 10.007544, 12.864445, 7.0854006, 9.161785, 14.450766, 7.1982546, 10.098274, 13.885704, 7.279489, 10.325115, 13.7075405, 7.556304, 10.0746155, 13.885704, 7.279489, 10.325115, 14.450766, 7.1982546, 10.098274, 14.443308, 7.0981884, 10.231224, 13.885704, 7.279489, 10.325115, 14.443308, 7.0981884, 10.231224, 14.063122, 7.117878, 10.426937, 11.658583, 6.90143, 9.442226, 12.092066, 6.8811474, 9.071436, 11.933365, 7.3444357, 9.894124, 11.658583, 6.90143, 9.442226, 11.79088, 6.773697, 9.139975, 12.092066, 6.8811474, 9.071436, 11.422882, 6.7517605, 9.392457, 11.603992, 6.6508856, 9.16963, 11.658583, 6.90143, 9.442226, 11.422882, 6.7517605, 9.392457, 11.497446, 6.636342, 9.199318, 11.603992, 6.6508856, 9.16963, 11.422882, 6.7517605, 9.392457, 11.151068, 6.834375, 9.774982, 11.28582, 6.5728126, 9.250239, 10.868927, 6.502743, 9.566353, 11.28582, 6.5728126, 9.250239, 11.151068, 6.834375, 9.774982, 10.868927, 6.502743, 9.566353, 10.970734, 6.4467087, 9.364567, 11.28582, 6.5728126, 9.250239, 14.070913, 7.495411, 9.618036, 14.482357, 7.125392, 10.12579, 14.3754425, 7.195709, 10.181591, 14.070913, 7.495411, 9.618036, 14.516771, 7.173098, 9.291218, 14.482357, 7.125392, 10.12579, 13.583624, 7.1059093, 9.775603, 13.968065, 7.2646875, 9.861715, 13.816387, 7.1105375, 9.930347, 13.583624, 7.1059093, 9.775603, 14.070913, 7.495411, 9.618036, 13.968065, 7.2646875, 9.861715, 12.772589, 6.376586, 6.9820247, 13.099979, 6.904664, 7.5420585, 12.775024, 6.637413, 7.820493, 12.772589, 6.376586, 6.9820247, 12.839212, 6.4047265, 6.9280143, 13.099979, 6.904664, 7.5420585, 13.099979, 6.904664, 7.5420585, 12.79912, 6.699265, 8.027189, 12.775024, 6.637413, 7.820493, 13.099979, 6.904664, 7.5420585, 13.025673, 6.931345, 7.982105, 12.79912, 6.699265, 8.027189, 13.025673, 6.931345, 7.982105, 13.099979, 6.904664, 7.5420585, 13.384867, 7.3345337, 8.193618, 13.025673, 6.931345, 7.982105, 13.384867, 7.3345337, 8.193618, 12.864683, 6.8550787, 8.580061, 13.025673, 6.931345, 7.982105, 12.864683, 6.8550787, 8.580061, 12.820887, 6.753347, 8.212795, 12.864683, 6.8550787, 8.580061, 13.068455, 7.0369205, 8.661718, 12.906773, 6.8680534, 8.665814, 12.864683, 6.8550787, 8.580061, 13.384867, 7.3345337, 8.193618, 13.068455, 7.0369205, 8.661718, 13.15687, 7.046695, 9.214457, 13.535355, 7.2501936, 9.400614, 13.26737, 7.0566998, 9.358015, 13.15687, 7.046695, 9.214457, 13.745743, 7.551609, 8.966543, 13.535355, 7.2501936, 9.400614, 12.922939, 6.384966, 6.9034257, 13.099979, 6.904664, 7.5420585, 12.839212, 6.4047265, 6.9280143, 13.099979, 6.904664, 7.5420585, 12.922939, 6.384966, 6.9034257, 13.11478, 6.619231, 7.236095, 13.11478, 6.619231, 7.236095, 12.922939, 6.384966, 6.9034257, 13.179651, 6.491927, 7.1159973, 13.099979, 6.904664, 7.5420585, 13.577553, 6.959513, 7.7937083, 13.384867, 7.3345337, 8.193618, 13.099979, 6.904664, 7.5420585, 13.501598, 6.832964, 7.5911536, 13.577553, 6.959513, 7.7937083, 14.043669, 6.938242, 7.9677033, 13.745743, 7.551609, 8.966543, 13.384867, 7.3345337, 8.193618, 13.745743, 7.551609, 8.966543, 14.043669, 6.938242, 7.9677033, 14.200254, 7.008704, 8.294954, 13.745743, 7.551609, 8.966543, 14.200254, 7.008704, 8.294954, 14.049522, 7.2166758, 8.535611, 14.115025, 7.3930063, 9.158257, 14.070913, 7.495411, 9.618036, 13.745743, 7.551609, 8.966543, 14.070913, 7.495411, 9.618036, 14.115025, 7.3930063, 9.158257, 14.474133, 7.170848, 9.13312, 14.070913, 7.495411, 9.618036, 14.474133, 7.170848, 9.13312, 14.516771, 7.173098, 9.291218, 13.745743, 7.551609, 8.966543, 13.068455, 7.0369205, 8.661718, 13.384867, 7.3345337, 8.193618, 13.402321, 7.300085, 8.886555, 13.068455, 7.0369205, 8.661718, 13.745743, 7.551609, 8.966543, 13.068455, 7.0369205, 8.661718, 13.402321, 7.300085, 8.886555, 13.049887, 6.9149427, 8.958126, 13.068455, 7.0369205, 8.661718, 13.049887, 6.9149427, 8.958126, 12.977623, 6.9004726, 8.814155, 13.745743, 7.551609, 8.966543, 13.079823, 7.007822, 9.05171, 13.402321, 7.300085, 8.886555, 13.745743, 7.551609, 8.966543, 13.15687, 7.046695, 9.214457, 13.079823, 7.007822, 9.05171, 14.049522, 7.2166758, 8.535611, 14.337751, 7.1039696, 8.595517, 13.745743, 7.551609, 8.966543, 14.049522, 7.2166758, 8.535611, 14.270794, 7.0704823, 8.454195, 14.337751, 7.1039696, 8.595517, 14.070913, 7.495411, 9.618036, 13.535355, 7.2501936, 9.400614, 13.745743, 7.551609, 8.966543, 13.535355, 7.2501936, 9.400614, 14.070913, 7.495411, 9.618036, 13.583624, 7.1059093, 9.775603, 13.535355, 7.2501936, 9.400614, 13.583624, 7.1059093, 9.775603, 13.427256, 7.094145, 9.573523, 14.115025, 7.3930063, 9.158257, 14.337751, 7.1039696, 8.595517, 14.450401, 7.090761, 9.003662, 14.115025, 7.3930063, 9.158257, 13.745743, 7.551609, 8.966543, 14.337751, 7.1039696, 8.595517, 14.3754425, 7.195709, 10.181591, 13.968065, 7.2646875, 9.861715, 14.070913, 7.495411, 9.618036, 13.968065, 7.2646875, 9.861715, 14.3754425, 7.195709, 10.181591, 14.275515, 7.113496, 10.233852, 13.968065, 7.2646875, 9.861715, 14.275515, 7.113496, 10.233852, 13.9707985, 7.1304455, 10.03817, 13.577553, 6.959513, 7.7937083, 14.043669, 6.938242, 7.9677033, 13.384867, 7.3345337, 8.193618, 13.577553, 6.959513, 7.7937083, 13.853798, 6.849599, 7.7665267, 14.043669, 6.938242, 7.9677033, 13.501598, 6.832964, 7.5911536, 13.736696, 6.747884, 7.6274853, 13.577553, 6.959513, 7.7937083, 13.501598, 6.832964, 7.5911536, 13.669174, 6.7349615, 7.561953, 13.736696, 6.747884, 7.6274853, 13.501598, 6.832964, 7.5911536, 13.099979, 6.904664, 7.5420585, 13.535788, 6.6803236, 7.4230866, 13.11478, 6.619231, 7.236095, 13.535788, 6.6803236, 7.4230866, 13.099979, 6.904664, 7.5420585, 13.11478, 6.619231, 7.236095, 13.306187, 6.5714235, 7.228906, 13.535788, 6.6803236, 7.4230866, 14.818359, 7.5795794, 9.704287, 14.319729, 7.1040306, 10.273739, 14.231741, 7.193877, 10.164581, 14.818359, 7.5795794, 9.704287, 15.293608, 7.170624, 10.134708, 14.319729, 7.1040306, 10.273739, 14.53705, 7.1410728, 9.124798, 14.520617, 7.3097606, 9.621042, 14.407236, 7.130357, 9.437237, 14.53705, 7.1410728, 9.124798, 14.818359, 7.5795794, 9.704287, 14.520617, 7.3097606, 9.621042, 17.50299, 6.374856, 7.3705115, 16.95148, 6.951913, 7.9566584, 16.564781, 6.648938, 7.610077, 17.50299, 6.374856, 7.3705115, 17.578213, 6.4056153, 7.4395566, 16.95148, 6.951913, 7.9566584, 16.95148, 6.951913, 7.9566584, 16.337984, 6.7154865, 7.697535, 16.564781, 6.648938, 7.610077, 16.95148, 6.951913, 7.9566584, 16.44146, 6.978112, 7.98315, 16.337984, 6.7154865, 7.697535, 16.44146, 6.978112, 7.98315, 16.95148, 6.951913, 7.9566584, 16.283978, 7.4333143, 8.50886, 16.44146, 6.978112, 7.98315, 16.283978, 7.4333143, 8.50886, 15.729202, 6.886676, 7.930798, 16.44146, 6.978112, 7.98315, 15.729202, 6.886676, 7.930798, 16.134073, 6.774377, 7.7760997, 15.729202, 6.886676, 7.930798, 15.686707, 7.0922894, 8.219696, 15.639549, 6.8975234, 8.001675, 15.729202, 6.886676, 7.930798, 16.283978, 7.4333143, 8.50886, 15.686707, 7.0922894, 8.219696, 15.082333, 7.0959883, 8.468272, 14.956093, 7.31973, 8.992256, 14.942156, 7.0999613, 8.635822, 15.082333, 7.0959883, 8.468272, 15.490726, 7.667551, 9.160905, 14.956093, 7.31973, 8.992256, 17.624905, 6.3763094, 7.5322666, 16.95148, 6.951913, 7.9566584, 17.578213, 6.4056153, 7.4395566, 16.95148, 6.951913, 7.9566584, 17.624905, 6.3763094, 7.5322666, 17.296997, 6.6220546, 7.8690057, 17.296997, 6.6220546, 7.8690057, 17.624905, 6.3763094, 7.5322666, 17.441767, 6.4720078, 7.9004264, 16.95148, 6.951913, 7.9566584, 16.778732, 6.9787436, 8.60397, 16.283978, 7.4333143, 8.50886, 16.95148, 6.951913, 7.9566584, 16.988663, 6.836954, 8.447882, 16.778732, 6.9787436, 8.60397, 16.685303, 6.91991, 9.205566, 15.490726, 7.667551, 9.160905, 16.283978, 7.4333143, 8.50886, 15.490726, 7.667551, 9.160905, 16.685303, 6.91991, 9.205566, 16.34667, 6.989422, 9.478991, 15.490726, 7.667551, 9.160905, 16.34667, 6.989422, 9.478991, 16.0481, 7.250002, 9.387381, 15.358458, 7.458866, 9.642784, 14.818359, 7.5795794, 9.704287, 15.490726, 7.667551, 9.160905, 14.818359, 7.5795794, 9.704287, 15.358458, 7.458866, 9.642784, 15.46587, 7.1714168, 10.046328, 14.818359, 7.5795794, 9.704287, 15.46587, 7.1714168, 10.046328, 15.293608, 7.170624, 10.134708, 15.490726, 7.667551, 9.160905, 15.686707, 7.0922894, 8.219696, 16.283978, 7.4333143, 8.50886, 15.509426, 7.387882, 8.707602, 15.686707, 7.0922894, 8.219696, 15.490726, 7.667551, 9.160905, 15.686707, 7.0922894, 8.219696, 15.509426, 7.387882, 8.707602, 15.334177, 6.938472, 8.243979, 15.686707, 7.0922894, 8.219696, 15.334177, 6.938472, 8.243979, 15.486206, 6.9294643, 8.125624, 15.490726, 7.667551, 9.160905, 15.250263, 7.0548754, 8.33129, 15.509426, 7.387882, 8.707602, 15.490726, 7.667551, 9.160905, 15.082333, 7.0959883, 8.468272, 15.250263, 7.0548754, 8.33129, 16.0481, 7.250002, 9.387381, 16.040924, 7.0956764, 9.734488, 15.490726, 7.667551, 9.160905, 16.0481, 7.250002, 9.387381, 16.186634, 7.0604343, 9.615782, 16.040924, 7.0956764, 9.734488, 14.818359, 7.5795794, 9.704287, 14.956093, 7.31973, 8.992256, 15.490726, 7.667551, 9.160905, 14.956093, 7.31973, 8.992256, 14.818359, 7.5795794, 9.704287, 14.53705, 7.1410728, 9.124798, 14.956093, 7.31973, 8.992256, 14.53705, 7.1410728, 9.124798, 14.7350445, 7.1373453, 8.888584, 15.358458, 7.458866, 9.642784, 16.040924, 7.0956764, 9.734488, 15.592907, 7.0656147, 9.958109, 15.358458, 7.458866, 9.642784, 15.490726, 7.667551, 9.160905, 16.040924, 7.0956764, 9.734488, 14.231741, 7.193877, 10.164581, 14.520617, 7.3097606, 9.621042, 14.818359, 7.5795794, 9.704287, 14.520617, 7.3097606, 9.621042, 14.231741, 7.193877, 10.164581, 14.1518955, 7.1016655, 10.051222, 14.520617, 7.3097606, 9.621042, 14.1518955, 7.1016655, 10.051222, 14.318668, 7.147036, 9.652245, 16.778732, 6.9787436, 8.60397, 16.685303, 6.91991, 9.205566, 16.283978, 7.4333143, 8.50886, 16.778732, 6.9787436, 8.60397, 16.869965, 6.8293467, 8.917667, 16.685303, 6.91991, 9.205566, 16.988663, 6.836954, 8.447882, 16.991056, 6.717228, 8.721294, 16.778732, 6.9787436, 8.60397, 16.988663, 6.836954, 8.447882, 17.05367, 6.7085714, 8.62688, 16.991056, 6.717228, 8.721294, 16.988663, 6.836954, 8.447882, 16.95148, 6.951913, 7.9566584, 17.18201, 6.656856, 8.4283905, 17.296997, 6.6220546, 7.8690057, 17.18201, 6.656856, 8.4283905, 16.95148, 6.951913, 7.9566584, 17.296997, 6.6220546, 7.8690057, 17.347916, 6.550868, 8.092527, 17.18201, 6.656856, 8.4283905, 14.640917, 7.231346, 10.77033, 14.15225, 7.201992, 10.013128, 14.311281, 7.246917, 9.992706, 14.640917, 7.231346, 10.77033, 13.950329, 6.9789553, 10.945974, 14.15225, 7.201992, 10.013128, 15.086668, 6.645689, 10.496582, 14.715862, 7.027137, 10.425049, 14.856575, 6.812086, 10.301319, 15.086668, 6.645689, 10.496582, 14.640917, 7.231346, 10.77033, 14.715862, 7.027137, 10.425049, 15.405359, 4.506928, 13.294855, 15.268087, 5.398923, 12.871037, 15.5953045, 5.062126, 12.496258, 15.405359, 4.506928, 13.294855, 15.330811, 4.5490294, 13.357082, 15.268087, 5.398923, 12.871037, 15.268087, 5.398923, 12.871037, 15.614843, 5.2105064, 12.296331, 15.5953045, 5.062126, 12.496258, 15.268087, 5.398923, 12.871037, 15.418043, 5.546952, 12.416988, 15.614843, 5.2105064, 12.296331, 15.418043, 5.546952, 12.416988, 15.268087, 5.398923, 12.871037, 15.162661, 6.218977, 12.31654, 15.418043, 5.546952, 12.416988, 15.162661, 6.218977, 12.31654, 15.6678505, 5.60172, 11.757086, 15.418043, 5.546952, 12.416988, 15.6678505, 5.60172, 11.757086, 15.632406, 5.343074, 12.11624, 15.6678505, 5.60172, 11.757086, 15.4920025, 5.9232817, 11.72563, 15.641216, 5.6641035, 11.663549, 15.6678505, 5.60172, 11.757086, 15.162661, 6.218977, 12.31654, 15.4920025, 5.9232817, 11.72563, 15.469013, 6.1774178, 11.125845, 15.1226845, 6.643654, 10.969555, 15.370789, 6.2906885, 10.961456, 15.469013, 6.1774178, 11.125845, 14.925304, 6.9021482, 11.535374, 15.1226845, 6.643654, 10.969555, 15.222748, 4.557292, 13.366293, 15.268087, 5.398923, 12.871037, 15.330811, 4.5490294, 13.357082, 15.268087, 5.398923, 12.871037, 15.222748, 4.557292, 13.366293, 15.117676, 4.9987955, 13.081551, 15.117676, 4.9987955, 13.081551, 15.222748, 4.557292, 13.366293, 14.994282, 4.8528996, 13.149015, 15.268087, 5.398923, 12.871037, 14.755228, 5.765085, 12.572195, 15.162661, 6.218977, 12.31654, 15.268087, 5.398923, 12.871037, 14.77644, 5.523675, 12.745871, 14.755228, 5.765085, 12.572195, 14.232555, 6.018609, 12.3237705, 14.925304, 6.9021482, 11.535374, 15.162661, 6.218977, 12.31654, 14.925304, 6.9021482, 11.535374, 14.232555, 6.018609, 12.3237705, 14.130428, 6.281682, 11.9799385, 14.925304, 6.9021482, 11.535374, 14.130428, 6.281682, 11.9799385, 14.391569, 6.5280485, 11.829561, 14.462694, 6.9798803, 11.226421, 14.640917, 7.231346, 10.77033, 14.925304, 6.9021482, 11.535374, 14.640917, 7.231346, 10.77033, 14.462694, 6.9798803, 11.226421, 13.968893, 6.899996, 11.121994, 14.640917, 7.231346, 10.77033, 13.968893, 6.899996, 11.121994, 13.950329, 6.9789553, 10.945974, 14.925304, 6.9021482, 11.535374, 15.4920025, 5.9232817, 11.72563, 15.162661, 6.218977, 12.31654, 15.217165, 6.445463, 11.55835, 15.4920025, 5.9232817, 11.72563, 14.925304, 6.9021482, 11.535374, 15.4920025, 5.9232817, 11.72563, 15.217165, 6.445463, 11.55835, 15.550241, 5.8804913, 11.346338, 15.4920025, 5.9232817, 11.72563, 15.550241, 5.8804913, 11.346338, 15.594999, 5.783677, 11.508028, 14.925304, 6.9021482, 11.535374, 15.519932, 6.0406504, 11.294071, 15.217165, 6.445463, 11.55835, 14.925304, 6.9021482, 11.535374, 15.469013, 6.1774178, 11.125845, 15.519932, 6.0406504, 11.294071, 14.391569, 6.5280485, 11.829561, 14.036055, 6.5608177, 11.683962, 14.925304, 6.9021482, 11.535374, 14.391569, 6.5280485, 11.829561, 14.080282, 6.442515, 11.830221, 14.036055, 6.5608177, 11.683962, 14.640917, 7.231346, 10.77033, 15.1226845, 6.643654, 10.969555, 14.925304, 6.9021482, 11.535374, 15.1226845, 6.643654, 10.969555, 14.640917, 7.231346, 10.77033, 15.086668, 6.645689, 10.496582, 15.1226845, 6.643654, 10.969555, 15.086668, 6.645689, 10.496582, 15.225153, 6.48761, 10.729311, 14.462694, 6.9798803, 11.226421, 14.036055, 6.5608177, 11.683962, 13.989519, 6.7390223, 11.216955, 14.462694, 6.9798803, 11.226421, 14.925304, 6.9021482, 11.535374, 14.036055, 6.5608177, 11.683962, 14.311281, 7.246917, 9.992706, 14.715862, 7.027137, 10.425049, 14.640917, 7.231346, 10.77033, 14.715862, 7.027137, 10.425049, 14.311281, 7.246917, 9.992706, 14.403456, 7.132376, 9.914658, 14.715862, 7.027137, 10.425049, 14.403456, 7.132376, 9.914658, 14.700848, 6.947154, 10.176794, 14.755228, 5.765085, 12.572195, 14.232555, 6.018609, 12.3237705, 15.162661, 6.218977, 12.31654, 14.755228, 5.765085, 12.572195, 14.395441, 5.764677, 12.524948, 14.232555, 6.018609, 12.3237705, 14.77644, 5.523675, 12.745871, 14.502789, 5.5489907, 12.638502, 14.755228, 5.765085, 12.572195, 14.77644, 5.523675, 12.745871, 14.557829, 5.4834146, 12.714375, 14.502789, 5.5489907, 12.638502, 14.77644, 5.523675, 12.745871, 15.268087, 5.398923, 12.871037, 14.671074, 5.3173504, 12.858131, 15.117676, 4.9987955, 13.081551, 14.671074, 5.3173504, 12.858131, 15.268087, 5.398923, 12.871037, 15.117676, 4.9987955, 13.081551, 14.877726, 5.0318, 13.048248, 14.671074, 5.3173504, 12.858131, 15.855707, 1.5785854, 8.303576, 15.537302, 3.513335, 8.969594, 15.675556, 3.59141, 8.79463, 15.855707, 1.5785854, 8.303576, 15.684317, 1.4956186, 8.515871, 15.537302, 3.513335, 8.969594, 15.718592, -0.6945374, 7.979791, 15.684317, 1.4956186, 8.515871, 15.855707, 1.5785854, 8.303576, 15.718592, -0.6945374, 7.979791, 15.528423, -0.7451041, 8.231567, 15.684317, 1.4956186, 8.515871, 15.190344, 1.4642165, 8.406557, 15.070126, 3.5373333, 8.645098, 15.119511, 3.4761071, 8.8678665, 15.190344, 1.4642165, 8.406557, 15.135492, 1.5326246, 8.145638, 15.070126, 3.5373333, 8.645098, 15.578283, 1.6655294, 7.8817935, 15.624922, 3.6523986, 8.571777, 15.435907, 3.6665154, 8.430279, 15.578283, 1.6655294, 7.8817935, 15.805153, 1.6476903, 8.039993, 15.624922, 3.6523986, 8.571777, 15.119511, 3.4761071, 8.8678665, 14.720741, 5.3593645, 9.339607, 14.75539, 5.3158965, 9.486606, 15.119511, 3.4761071, 8.8678665, 15.070126, 3.5373333, 8.645098, 14.720741, 5.3593645, 9.339607, 15.675556, 3.59141, 8.79463, 15.031363, 5.349116, 9.558678, 15.124321, 5.4062643, 9.4429035, 15.675556, 3.59141, 8.79463, 15.537302, 3.513335, 8.969594, 15.031363, 5.349116, 9.558678, 15.124321, 5.4062643, 9.4429035, 15.031363, 5.349116, 9.558678, 14.700575, 6.30957, 9.911303, 15.124321, 5.4062643, 9.4429035, 14.700575, 6.30957, 9.911303, 14.765755, 6.3641186, 9.829805, 15.684317, 1.4956186, 8.515871, 15.309, 3.4624698, 9.008275, 15.537302, 3.513335, 8.969594, 15.684317, 1.4956186, 8.515871, 15.412914, 1.4456806, 8.567421, 15.309, 3.4624698, 9.008275, 15.537302, 3.513335, 8.969594, 15.309, 3.4624698, 9.008275, 14.880146, 5.309407, 9.581961, 15.537302, 3.513335, 8.969594, 14.880146, 5.309407, 9.581961, 15.031363, 5.349116, 9.558678, 15.135492, 1.5326246, 8.145638, 15.208366, 3.6164234, 8.469492, 15.070126, 3.5373333, 8.645098, 15.135492, 1.5326246, 8.145638, 15.30286, 1.6151665, 7.9359193, 15.208366, 3.6164234, 8.469492, 15.070126, 3.5373333, 8.645098, 15.208366, 3.6164234, 8.469492, 14.811323, 5.4180756, 9.224138, 15.070126, 3.5373333, 8.645098, 14.811323, 5.4180756, 9.224138, 14.720741, 5.3593645, 9.339607, 15.435907, 3.6665154, 8.430279, 15.090517, 5.4490795, 9.293634, 14.963499, 5.456666, 9.198397, 15.435907, 3.6665154, 8.430279, 15.624922, 3.6523986, 8.571777, 15.090517, 5.4490795, 9.293634, 15.805153, 1.6476903, 8.039993, 15.675556, 3.59141, 8.79463, 15.624922, 3.6523986, 8.571777, 15.805153, 1.6476903, 8.039993, 15.855707, 1.5785854, 8.303576, 15.675556, 3.59141, 8.79463, 15.624922, 3.6523986, 8.571777, 15.124321, 5.4062643, 9.4429035, 15.090517, 5.4490795, 9.293634, 15.624922, 3.6523986, 8.571777, 15.675556, 3.59141, 8.79463, 15.124321, 5.4062643, 9.4429035, 15.30286, 1.6151665, 7.9359193, 15.435907, 3.6665154, 8.430279, 15.208366, 3.6164234, 8.469492, 15.30286, 1.6151665, 7.9359193, 15.578283, 1.6655294, 7.8817935, 15.435907, 3.6665154, 8.430279, 15.080032, -0.60457873, 7.549851, 15.578283, 1.6655294, 7.8817935, 15.30286, 1.6151665, 7.9359193, 15.080032, -0.60457873, 7.549851, 15.392487, -0.5923369, 7.483996, 15.578283, 1.6655294, 7.8817935, 15.208366, 3.6164234, 8.469492, 14.963499, 5.456666, 9.198397, 14.811323, 5.4180756, 9.224138, 15.208366, 3.6164234, 8.469492, 15.435907, 3.6665154, 8.430279, 14.963499, 5.456666, 9.198397, 14.811323, 5.4180756, 9.224138, 14.963499, 5.456666, 9.198397, 14.646309, 6.401553, 9.648203, 14.811323, 5.4180756, 9.224138, 14.646309, 6.401553, 9.648203, 14.5366535, 6.3609424, 9.662085, 14.498898, 6.2705493, 9.8520975, 14.315634, 6.982005, 10.216946, 14.590864, 6.2697954, 9.924797, 14.498898, 6.2705493, 9.8520975, 14.248718, 6.9810524, 10.163109, 14.315634, 6.982005, 10.216946, 14.700575, 6.30957, 9.911303, 14.437746, 7.065465, 10.1538315, 14.765755, 6.3641186, 9.829805, 14.700575, 6.30957, 9.911303, 14.392586, 7.019636, 10.210747, 14.437746, 7.065465, 10.1538315, 14.590864, 6.2697954, 9.924797, 14.392586, 7.019636, 10.210747, 14.700575, 6.30957, 9.911303, 14.590864, 6.2697954, 9.924797, 14.315634, 6.982005, 10.216946, 14.392586, 7.019636, 10.210747, 14.73908, 6.400427, 9.720892, 14.35066, 7.092757, 10.021431, 14.646309, 6.401553, 9.648203, 14.73908, 6.400427, 9.720892, 14.416679, 7.0957565, 10.076281, 14.35066, 7.092757, 10.021431, 14.765755, 6.3641186, 9.829805, 14.416679, 7.0957565, 10.076281, 14.73908, 6.400427, 9.720892, 14.765755, 6.3641186, 9.829805, 14.437746, 7.065465, 10.1538315, 14.416679, 7.0957565, 10.076281, 14.5366535, 6.3609424, 9.662085, 14.22855, 7.0092974, 10.084545, 14.471951, 6.307063, 9.744005, 14.5366535, 6.3609424, 9.662085, 14.27281, 7.057174, 10.028643, 14.22855, 7.0092974, 10.084545, 14.646309, 6.401553, 9.648203, 14.27281, 7.057174, 10.028643, 14.5366535, 6.3609424, 9.662085, 14.646309, 6.401553, 9.648203, 14.35066, 7.092757, 10.021431, 14.27281, 7.057174, 10.028643, 15.214357, -0.7575648, 8.298452, 15.190344, 1.4642165, 8.406557, 15.412914, 1.4456806, 8.567421, 15.214357, -0.7575648, 8.298452, 14.957146, -0.7182157, 8.110065, 15.190344, 1.4642165, 8.406557, 14.471951, 6.307063, 9.744005, 14.248718, 6.9810524, 10.163109, 14.498898, 6.2705493, 9.8520975, 14.471951, 6.307063, 9.744005, 14.22855, 7.0092974, 10.084545, 14.248718, 6.9810524, 10.163109, 15.412914, 1.4456806, 8.567421, 15.119511, 3.4761071, 8.8678665, 15.309, 3.4624698, 9.008275, 15.412914, 1.4456806, 8.567421, 15.190344, 1.4642165, 8.406557, 15.119511, 3.4761071, 8.8678665, 14.957146, -0.7182157, 8.110065, 15.135492, 1.5326246, 8.145638, 15.190344, 1.4642165, 8.406557, 14.957146, -0.7182157, 8.110065, 14.888254, -0.6553638, 7.8026576, 15.135492, 1.5326246, 8.145638, 15.392487, -0.5923369, 7.483996, 15.805153, 1.6476903, 8.039993, 15.578283, 1.6655294, 7.8817935, 15.392487, -0.5923369, 7.483996, 15.648527, -0.6318462, 7.673121, 15.805153, 1.6476903, 8.039993, 15.528423, -0.7451041, 8.231567, 15.412914, 1.4456806, 8.567421, 15.684317, 1.4956186, 8.515871, 15.528423, -0.7451041, 8.231567, 15.214357, -0.7575648, 8.298452, 15.412914, 1.4456806, 8.567421, 14.888254, -0.6553638, 7.8026576, 15.30286, 1.6151665, 7.9359193, 15.135492, 1.5326246, 8.145638, 14.888254, -0.6553638, 7.8026576, 15.080032, -0.60457873, 7.549851, 15.30286, 1.6151665, 7.9359193, 15.648527, -0.6318462, 7.673121, 15.855707, 1.5785854, 8.303576, 15.805153, 1.6476903, 8.039993, 15.648527, -0.6318462, 7.673121, 15.718592, -0.6945374, 7.979791, 15.855707, 1.5785854, 8.303576, 15.309, 3.4624698, 9.008275, 14.75539, 5.3158965, 9.486606, 14.880146, 5.309407, 9.581961, 15.309, 3.4624698, 9.008275, 15.119511, 3.4761071, 8.8678665, 14.75539, 5.3158965, 9.486606, 14.880146, 5.309407, 9.581961, 14.498898, 6.2705493, 9.8520975, 14.590864, 6.2697954, 9.924797, 14.880146, 5.309407, 9.581961, 14.75539, 5.3158965, 9.486606, 14.498898, 6.2705493, 9.8520975, 14.75539, 5.3158965, 9.486606, 14.471951, 6.307063, 9.744005, 14.498898, 6.2705493, 9.8520975, 14.75539, 5.3158965, 9.486606, 14.720741, 5.3593645, 9.339607, 14.471951, 6.307063, 9.744005, 15.031363, 5.349116, 9.558678, 14.590864, 6.2697954, 9.924797, 14.700575, 6.30957, 9.911303, 15.031363, 5.349116, 9.558678, 14.880146, 5.309407, 9.581961, 14.590864, 6.2697954, 9.924797, 14.963499, 5.456666, 9.198397, 14.73908, 6.400427, 9.720892, 14.646309, 6.401553, 9.648203, 14.963499, 5.456666, 9.198397, 15.090517, 5.4490795, 9.293634, 14.73908, 6.400427, 9.720892, 15.090517, 5.4490795, 9.293634, 14.765755, 6.3641186, 9.829805, 14.73908, 6.400427, 9.720892, 15.090517, 5.4490795, 9.293634, 15.124321, 5.4062643, 9.4429035, 14.765755, 6.3641186, 9.829805, 14.720741, 5.3593645, 9.339607, 14.5366535, 6.3609424, 9.662085, 14.471951, 6.307063, 9.744005, 14.720741, 5.3593645, 9.339607, 14.811323, 5.4180756, 9.224138, 14.5366535, 6.3609424, 9.662085, -18.8297, 3.3895998, -9.473499, -19.616299, 3.2075999, -9.711899, -19.1775, 3.1662998, -9.7628, -18.8297, 3.3895998, -9.473499, -19.3743, 3.391, -9.417299, -19.616299, 3.2075999, -9.711899, -19.3743, 3.391, -9.417299, -19.611, 3.187, -9.246899, -19.616299, 3.2075999, -9.711899, 10.0335, 3.3913999, -10.0343, 13.378099, 3.3913999, -13.378599, 13.378099, 3.3913999, -10.0343, 13.378099, 3.3913999, -13.378599, 16.722599, 3.3913999, -10.0343, 13.378099, 3.3913999, -10.0343, 13.378099, 3.3913999, -6.6894, 13.378099, 3.3913999, -10.0343, 16.722599, 3.3913999, -10.0343, 13.378099, 3.3913999, -6.6894, 16.722599, 3.3913999, -10.0343, 16.722599, 3.3913999, -6.6894, 10.0335, 3.3913999, -6.6894, 13.378099, 3.3913999, -10.0343, 13.378099, 3.3913999, -6.6894, 10.0335, 3.3913999, -6.6894, 10.0335, 3.3913999, -10.0343, 13.378099, 3.3913999, -10.0343, 16.722599, 3.3913999, -10.0343, 16.4746, 3.3913999, -13.3993, 16.722599, 3.3913999, -13.109999, 16.722599, 3.3913999, -10.0343, 13.378099, 3.3913999, -13.378599, 16.4746, 3.3913999, -13.3993, 16.4746, 3.3913999, -13.3993, 15.5536995, 3.3913999, -16.3177, 16.4952, 3.3913999, -15.5392, 16.4746, 3.3913999, -13.3993, 12.7383995, 3.3913999, -15.8953, 15.5536995, 3.3913999, -16.3177, 12.7383995, 3.3913999, -15.8953, 16.4746, 3.3913999, -13.3993, 13.378099, 3.3913999, -13.378599, 13.378099, 3.3913999, -13.378599, 12.460499, 3.3913999, -16.1674, 12.7383995, 3.3913999, -15.8953, 13.378099, 3.3913999, -13.378599, 10.0335, 3.3913999, -16.7235, 12.460499, 3.3913999, -16.1674, 10.0335, 3.3913999, -16.7235, 13.378099, 3.3913999, -13.378599, 10.0335, 3.3913999, -13.378599, 10.0335, 3.3913999, -10.0343, 10.0335, 3.3913999, -13.378599, 13.378099, 3.3913999, -13.378599, 6.6889997, 3.3913999, -13.378599, 10.0335, 3.3913999, -16.7235, 10.0335, 3.3913999, -13.378599, 6.6889997, 3.3913999, -13.378599, 6.6889997, 3.3913999, -16.7235, 10.0335, 3.3913999, -16.7235, 6.6889997, 3.3913999, -10.0343, 10.0335, 3.3913999, -13.378599, 10.0335, 3.3913999, -10.0343, 6.6889997, 3.3913999, -10.0343, 6.6889997, 3.3913999, -13.378599, 10.0335, 3.3913999, -13.378599, 4.6816, 3.3913999, -13.378599, 6.6889997, 3.3913999, -16.7235, 6.6889997, 3.3913999, -13.378599, 4.6816, 3.3913999, -13.378599, 3.8384998, 3.3913999, -16.7235, 6.6889997, 3.3913999, -16.7235, 4.5101, 3.3913999, -8.955799, 4.6816, 3.3913999, -13.378599, 6.6889997, 3.3913999, -13.378599, 4.5101, 3.3913999, -8.955799, 6.6889997, 3.3913999, -13.378599, 6.6889997, 3.3913999, -10.0343, 4.6816, 3.3913999, -13.378599, 4.5101, 3.3913999, -8.955799, 4.1105, 3.3913999, -9.3386, 6.6889997, 3.3913999, -6.6894, 6.6889997, 3.3913999, -10.0343, 10.0335, 3.3913999, -10.0343, 6.6889997, 3.3913999, -6.6894, 4.5101, 3.3913999, -8.955799, 6.6889997, 3.3913999, -10.0343, 6.6889997, 3.3913999, -6.6894, 10.0335, 3.3913999, -10.0343, 10.0335, 3.3913999, -6.6894, 4.1105, 3.3913999, -9.3386, 4.5101, 3.3913999, -8.955799, 3.8492, 3.3913999, -8.198, 6.6889997, 3.3913999, -6.6894, 3.8492, 3.3913999, -8.198, 4.5101, 3.3913999, -8.955799, 4.1105, 3.3913999, -9.3386, 3.8492, 3.3913999, -8.198, 3.4176, 3.3913999, -8.714399, 3.3444998, 3.3913999, -6.6894, 3.4176, 3.3913999, -8.714399, 3.8492, 3.3913999, -8.198, 3.8492, 3.3913999, -8.198, 6.6889997, 3.3913999, -6.6894, 3.3444998, 3.3913999, -6.6894, 3.3444998, 3.3913999, -6.6894, 0, 3.3913999, -8.714399, 3.4176, 3.3913999, -8.714399, 6.6889997, 3.3913999, -3.3451, 6.6889997, 3.3913999, -6.6894, 10.0335, 3.3913999, -6.6894, 6.6889997, 3.3913999, -3.3451, 10.0335, 3.3913999, -6.6894, 10.0335, 3.3913999, -3.3451, 3.3444998, 3.3913999, -3.3451, 3.3444998, 3.3913999, -6.6894, 6.6889997, 3.3913999, -6.6894, 3.3444998, 3.3913999, -3.3451, 6.6889997, 3.3913999, -6.6894, 6.6889997, 3.3913999, -3.3451, 0, 3.3913999, -8.714399, 3.3444998, 3.3913999, -6.6894, 0, 3.3913999, -6.6894, 0, 3.3913999, -6.6894, -3.5373998, 3.3913999, -9.750299, 0, 3.3913999, -8.714399, 0, 3.3913999, -3.3451, 3.3444998, 3.3913999, -6.6894, 3.3444998, 3.3913999, -3.3451, 0, 3.3913999, -3.3451, 0, 3.3913999, -6.6894, 3.3444998, 3.3913999, -6.6894, 3.3444998, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, -3.3451, 6.6889997, 3.3913999, -3.3451, 3.3444998, 3.3913999, -0.000100016594, 6.6889997, 3.3913999, -3.3451, 6.6889997, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, 3.3442, 3.3444998, 3.3913999, -0.000100016594, 6.6889997, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, 3.3442, 6.6889997, 3.3913999, -0.000100016594, 6.6889997, 3.3913999, 3.3442, 0, 3.3913999, 3.3442, 3.3444998, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, 3.3442, 3.3444998, 3.3913999, 6.6891, 3.3444998, 3.3913999, 3.3442, 6.6889997, 3.3913999, 3.3442, 0, 3.3913999, 3.3442, 0, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, -0.000100016594, 0, 3.3913999, -0.000100016594, 3.3444998, 3.3913999, -3.3451, 3.3444998, 3.3913999, -0.000100016594, 0, 3.3913999, -0.000100016594, 0, 3.3913999, -3.3451, 3.3444998, 3.3913999, -3.3451, -2.9569001, 3.3913999, 3.3442, 0, 3.3913999, -0.000100016594, 0, 3.3913999, 3.3442, -2.9569001, 3.3913999, 3.3442, -2.9569001, 3.3913999, -0.000100016594, 0, 3.3913999, -0.000100016594, -2.9569001, 3.3913999, -0.000100016594, 0, 3.3913999, -3.3451, 0, 3.3913999, -0.000100016594, -2.9569001, 3.3913999, -0.000100016594, -3.6383996, 3.3913999, -2.0795999, 0, 3.3913999, -3.3451, -3.6383996, 3.3913999, -2.0795999, 0, 3.3913999, -6.6894, 0, 3.3913999, -3.3451, -3.6383996, 3.3913999, -2.0795999, -3.3452, 3.3913999, -6.6894, 0, 3.3913999, -6.6894, -6.6896996, 3.3913999, -3.7331, -3.3452, 3.3913999, -6.6894, -3.6383996, 3.3913999, -2.0795999, -3.8484998, 3.3913999, -9.5107, 0, 3.3913999, -6.6894, -3.3452, 3.3913999, -6.6894, 0, 3.3913999, -6.6894, -3.8484998, 3.3913999, -9.5107, -3.5373998, 3.3913999, -9.750299, -3.5373998, 3.3913999, -9.750299, -3.8484998, 3.3913999, -9.5107, -4.2641, 3.3913999, -9.881699, -3.3452, 3.3913999, -6.6894, -4.2641, 3.3913999, -9.881699, -3.8484998, 3.3913999, -9.5107, -3.5373998, 3.3913999, -9.750299, -4.2641, 3.3913999, -9.881699, -3.9104, 3.3913999, -10.240799, -4.2641, 3.3913999, -9.881699, -3.9104, 3.3913999, -12.1610985, -3.9104, 3.3913999, -10.240799, -6.6896996, 3.3913999, -3.7331, -6.6896996, 3.3913999, -6.6894, -3.3452, 3.3913999, -6.6894, -10.034201, 3.3913999, -3.7331, -6.6896996, 3.3913999, -6.6894, -6.6896996, 3.3913999, -3.7331, -3.3452, 3.3913999, -6.6894, -6.6896996, 3.3913999, -10.0343, -4.2641, 3.3913999, -9.881699, -6.6896996, 3.3913999, -10.0343, -3.3452, 3.3913999, -6.6894, -6.6896996, 3.3913999, -6.6894, -4.2641, 3.3913999, -9.881699, -6.6896996, 3.3913999, -12.5385, -3.9104, 3.3913999, -12.1610985, -6.6896996, 3.3913999, -12.5385, -4.2641, 3.3913999, -9.881699, -6.6896996, 3.3913999, -10.0343, -3.9104, 3.3913999, -12.1610985, -6.6896996, 3.3913999, -12.5385, -4.2794, 3.3913999, -12.5385, -10.034201, 3.3913999, -3.7331, -10.034201, 3.3913999, -6.6894, -6.6896996, 3.3913999, -6.6894, -10.034201, 3.3913999, -6.6894, -6.6896996, 3.3913999, -10.0343, -6.6896996, 3.3913999, -6.6894, -13.378799, 3.3913999, -3.7331, -10.034201, 3.3913999, -6.6894, -10.034201, 3.3913999, -3.7331, -10.034201, 3.3913999, -10.0343, -6.6896996, 3.3913999, -12.5385, -6.6896996, 3.3913999, -10.0343, -10.034201, 3.3913999, -6.6894, -10.034201, 3.3913999, -10.0343, -6.6896996, 3.3913999, -10.0343, -10.034201, 3.3913999, -10.0343, -10.6998005, 3.3913999, -13.1763, -6.6896996, 3.3913999, -12.5385, -13.378799, 3.3913999, -3.7331, -13.378799, 3.3913999, -6.6894, -10.034201, 3.3913999, -6.6894, -13.378799, 3.3913999, -6.6894, -10.034201, 3.3913999, -10.0343, -10.034201, 3.3913999, -6.6894, -16.192, 3.3913999, -3.0972, -13.378799, 3.3913999, -6.6894, -13.378799, 3.3913999, -3.7331, -13.378799, 3.3913999, -10.0343, -10.6998005, 3.3913999, -13.1763, -10.034201, 3.3913999, -10.0343, -13.378799, 3.3913999, -6.6894, -13.378799, 3.3913999, -10.0343, -10.034201, 3.3913999, -10.0343, -13.378799, 3.3913999, -10.0343, -13.378799, 3.3913999, -13.378599, -10.6998005, 3.3913999, -13.1763, -16.192, 3.3913999, -3.0972, -16.723299, 3.3913999, -6.6894, -13.378799, 3.3913999, -6.6894, -19.754599, 3.3913999, -2.7854, -16.723299, 3.3913999, -6.6894, -16.192, 3.3913999, -3.0972, -19.754599, 3.3913999, -2.7854, -19.2812, 3.3913999, -6.6894, -16.723299, 3.3913999, -6.6894, -17.111599, 3.3913999, -0.000100016594, -19.754599, 3.3913999, -2.7854, -16.192, 3.3913999, -3.0972, -17.111599, 3.3913999, -0.000100016594, -19.9515, 3.3913999, -2.5836997, -19.754599, 3.3913999, -2.7854, -19.9515, 3.3913999, -2.5836997, -17.111599, 3.3913999, -0.000100016594, -20.068499, 3.3913999, -0.000100016594, -20.068499, 3.3913999, 3.3442, -20.068499, 3.3913999, -0.000100016594, -17.111599, 3.3913999, -0.000100016594, -20.068499, 3.3913999, 3.3442, -17.111599, 3.3913999, -0.000100016594, -17.111599, 3.3913999, 3.304, -20.068499, 3.3913999, 6.6891, -20.068499, 3.3913999, 3.3442, -17.111599, 3.3913999, 3.304, -22.332499, 3.3913999, 8.990399, -22.315199, 3.3913999, 6.6891, -20.068499, 3.3913999, 6.6891, -22.332499, 3.3913999, 8.990399, -20.068499, 3.3913999, 6.6891, -20.068499, 3.3913999, 9.1673, -20.068499, 3.3913999, 9.1673, -20.068499, 3.3913999, 6.6891, -16.723299, 3.3913999, 6.6891, -20.068499, 3.3913999, 6.6891, -17.111599, 3.3913999, 3.304, -16.723299, 3.3913999, 6.6891, -20.068499, 3.3913999, 9.1673, -16.723299, 3.3913999, 6.6891, -16.285099, 3.3913999, 9.5862, -16.723299, 3.3913999, 6.6891, -16.0544, 3.3913999, 9.808, -16.285099, 3.3913999, 9.5862, -16.0544, 3.3913999, 9.808, -16.723299, 3.3913999, 6.6891, -13.136101, 3.3913999, 6.7548, -17.111599, 3.3913999, 3.304, -13.136101, 3.3913999, 6.7548, -16.723299, 3.3913999, 6.6891, -16.0544, 3.3913999, 9.808, -13.136101, 3.3913999, 6.7548, -13.378799, 3.3913999, 10.0334, -13.378799, 3.3913999, 10.0334, -13.136101, 3.3913999, 6.7548, -10.034201, 3.3913999, 7.0772, -13.378799, 3.3913999, 10.0334, -10.034201, 3.3913999, 7.0772, -10.034201, 3.3913999, 10.0334, -13.136101, 3.3913999, 6.7548, -17.111599, 3.3913999, 3.304, -14.877501, 3.3913999, 4.6651998, -14.877501, 3.3913999, 4.6651998, -17.111599, 3.3913999, 3.304, -15.5823, 3.3913999, 4.0364, -13.136101, 3.3913999, 6.7548, -14.877501, 3.3913999, 4.6651998, -14.0949, 3.3913999, 5.5070996, 3.3444998, 3.3913999, 10.0334, 6.6889997, 3.3913999, 6.6891, 6.6889997, 3.3913999, 10.0334, 0, 3.3913999, 13.3783, 0, 3.3913999, 10.0334, 3.3444998, 3.3913999, 10.0334, 0, 3.3913999, 13.3783, 3.3444998, 3.3913999, 10.0334, 3.3444998, 3.3913999, 13.3783, 3.3444998, 3.3913999, 13.3783, 3.3444998, 3.3913999, 10.0334, 6.6889997, 3.3913999, 10.0334, 3.3444998, 3.3913999, 10.0334, 3.3444998, 3.3913999, 6.6891, 6.6889997, 3.3913999, 6.6891, 3.3444998, 3.3913999, 6.6891, 6.6889997, 3.3913999, 3.3442, 6.6889997, 3.3913999, 6.6891, 6.6889997, 3.3913999, 6.6891, 6.6889997, 3.3913999, 3.3442, 10.0335, 3.3913999, 4.2546997, 6.6889997, 3.3913999, 6.6891, 10.0335, 3.3913999, 4.2546997, 10.0335, 3.3913999, 6.6891, 6.6889997, 3.3913999, 10.0334, 6.6889997, 3.3913999, 6.6891, 10.0335, 3.3913999, 6.6891, 6.6889997, 3.3913999, 10.0334, 10.0335, 3.3913999, 6.6891, 10.0335, 3.3913999, 10.0334, 3.3444998, 3.3913999, 13.3783, 6.6889997, 3.3913999, 10.0334, 6.6889997, 3.3913999, 13.3783, 3.9143, 3.3913999, 16.5144, 3.3444998, 3.3913999, 13.3783, 6.6889997, 3.3913999, 13.3783, 3.3444998, 3.3913999, 13.3783, 3.9143, 3.3913999, 16.5144, 3.6397, 3.3913999, 16.2405, 3.9143, 3.3913999, 16.5144, 6.6889997, 3.3913999, 13.3783, 6.6889997, 3.3913999, 16.7227, -3.3452, 3.3913999, 13.3783, 0, 3.3913999, 10.0334, 0, 3.3913999, 13.3783, -3.3452, 3.3913999, 13.3783, -3.3452, 3.4796999, 10.0334, 0, 3.3913999, 10.0334, 0, 3.3913999, 10.0334, 0, 3.4796999, 6.6891, 3.3444998, 3.3913999, 6.6891, 0, 3.3913999, 10.0334, 3.3444998, 3.3913999, 6.6891, 3.3444998, 3.3913999, 10.0334, 0, 3.4796999, 6.6891, 3.3444998, 3.3913999, 3.3442, 3.3444998, 3.3913999, 6.6891, 0, 3.4796999, 6.6891, 0, 3.3913999, 3.3442, 3.3444998, 3.3913999, 3.3442, 6.6889997, 3.3913999, -0.000100016594, 10.0335, 3.3913999, -3.3451, 10.0335, 3.2212, -2.2310996, 6.6889997, 3.3913999, -0.000100016594, 6.6889997, 3.3913999, -3.3451, 10.0335, 3.3913999, -3.3451, -10.034201, 3.0033998, 6.6891, -13.136101, 3.3913999, 6.7548, -12.747801, 3.0033998, 6.3666997, -10.034201, 3.0033998, 6.6891, -10.034201, 3.3913999, 7.0772, -13.136101, 3.3913999, 6.7548, -2.5679002, 2.9782999, 7.6208997, -6.6896996, 3.4796999, 7.0772, -6.6896996, 3.0033998, 6.6891, -2.5679002, 2.9782999, 7.6208997, -2.1796002, 3.3107998, 8.009, -6.6896996, 3.4796999, 7.0772, -12.747801, 3.0033998, 6.3666997, -14.0949, 3.3913999, 5.5070996, -13.694601, 3.0033998, 5.1793, -12.747801, 3.0033998, 6.3666997, -13.136101, 3.3913999, 6.7548, -14.0949, 3.3913999, 5.5070996, -6.6896996, 3.0033998, 6.6891, -10.034201, 3.3913999, 7.0772, -10.034201, 3.0033998, 6.6891, -6.6896996, 3.0033998, 6.6891, -6.6896996, 3.4796999, 7.0772, -10.034201, 3.3913999, 7.0772, -16.723299, 3.0033998, -0.000100016594, -16.192, 3.3913999, -3.0972, -15.8036995, 3.0033998, -2.7097, -16.723299, 3.0033998, -0.000100016594, -17.111599, 3.3913999, -0.000100016594, -16.192, 3.3913999, -3.0972, -13.378799, 3.3913999, -3.7331, -15.8036995, 3.0033998, -2.7097, -16.192, 3.3913999, -3.0972, -13.378799, 3.3913999, -3.7331, -13.378799, 3.0033998, -3.3451, -15.8036995, 3.0033998, -2.7097, -2.9569001, 3.3913999, -0.000100016594, -4.0260997, 3.0033998, -1.6915, -3.6383996, 3.3913999, -2.0795999, -2.9569001, 3.3913999, -0.000100016594, -3.3452, 3.0033998, -0.000100016594, -4.0260997, 3.0033998, -1.6915, -3.6383996, 3.3913999, -2.0795999, -6.6896996, 3.0033998, -3.3451, -6.6896996, 3.3913999, -3.7331, -3.6383996, 3.3913999, -2.0795999, -4.0260997, 3.0033998, -1.6915, -6.6896996, 3.0033998, -3.3451, -10.034201, 3.3913999, -3.7331, -13.378799, 3.0033998, -3.3451, -13.378799, 3.3913999, -3.7331, -10.034201, 3.3913999, -3.7331, -10.034201, 3.0033998, -3.3451, -13.378799, 3.0033998, -3.3451, -6.6896996, 3.3913999, -3.7331, -10.034201, 3.0033998, -3.3451, -10.034201, 3.3913999, -3.7331, -6.6896996, 3.3913999, -3.7331, -6.6896996, 3.0033998, -3.3451, -10.034201, 3.0033998, -3.3451, -16.723299, 3.0033998, 2.9164999, -17.111599, 3.3913999, -0.000100016594, -16.723299, 3.0033998, -0.000100016594, -16.723299, 3.0033998, 2.9164999, -17.111599, 3.3913999, 3.304, -17.111599, 3.3913999, -0.000100016594, -14.4897995, 3.0033998, 4.2777, -15.5823, 3.3913999, 4.0364, -15.3722, 3.0033998, 3.6128, -14.4897995, 3.0033998, 4.2777, -14.877501, 3.3913999, 4.6651998, -15.5823, 3.3913999, 4.0364, -2.9569001, 3.3913999, 3.3442, -3.3452, 3.0033998, -0.000100016594, -2.9569001, 3.3913999, -0.000100016594, -2.9569001, 3.3913999, 3.3442, -3.3452, 3.0033998, 3.3442, -3.3452, 3.0033998, -0.000100016594, -2.1796002, 3.3107998, 8.009, -2.1397, 3.0033998, 5.1793, -1.7839999, 3.3913999, 5.216, -2.1796002, 3.3107998, 8.009, -2.5679002, 2.9782999, 7.6208997, -2.1397, 3.0033998, 5.1793, -6.6896996, 3.3913999, 10.0334, -2.1796002, 3.3107998, 8.009, -3.3452, 3.4796999, 10.0334, -6.6896996, 3.3913999, 10.0334, -6.6896996, 3.4796999, 7.0772, -2.1796002, 3.3107998, 8.009, -10.034201, 3.3913999, 10.0334, -6.6896996, 3.4796999, 7.0772, -6.6896996, 3.3913999, 10.0334, -10.034201, 3.3913999, 10.0334, -10.034201, 3.3913999, 7.0772, -6.6896996, 3.4796999, 7.0772, -1.7839999, 3.3913999, 5.216, 0, 3.4796999, 6.6891, -2.1796002, 3.3107998, 8.009, 0, 3.4796999, 6.6891, -1.7839999, 3.3913999, 5.216, -2.9569001, 3.3913999, 3.3442, 0, 3.4796999, 6.6891, -2.9569001, 3.3913999, 3.3442, 0, 3.3913999, 3.3442, -3.3452, 3.4796999, 10.0334, 0, 3.4796999, 6.6891, 0, 3.3913999, 10.0334, -3.3452, 3.4796999, 10.0334, -2.1796002, 3.3107998, 8.009, 0, 3.4796999, 6.6891, -2.1397, 3.0033998, 5.1793, -2.9569001, 3.3913999, 3.3442, -1.7839999, 3.3913999, 5.216, -2.1397, 3.0033998, 5.1793, -3.3452, 3.0033998, 3.3442, -2.9569001, 3.3913999, 3.3442, -14.0949, 3.3913999, 5.5070996, -14.4897995, 3.0033998, 4.2777, -13.694601, 3.0033998, 5.1793, -14.0949, 3.3913999, 5.5070996, -14.877501, 3.3913999, 4.6651998, -14.4897995, 3.0033998, 4.2777, -15.5823, 3.3913999, 4.0364, -16.723299, 3.0033998, 2.9164999, -15.3722, 3.0033998, 3.6128, -15.5823, 3.3913999, 4.0364, -17.111599, 3.3913999, 3.304, -16.723299, 3.0033998, 2.9164999, 16.722599, 3.3913999, -13.109999, 16.4746, 3.3913999, -13.3993, 16.8123, 3.1578999, -13.4637985, 10.0335, 3.0523999, -19.0296, 6.6797, 3.2777998, -18.734999, 6.6763997, 3.0570998, -19.0278, 10.0335, 3.0523999, -19.0296, 10.0335, 3.2784998, -18.7001, 6.6797, 3.2777998, -18.734999, 6.6889997, 3.3913999, -16.7235, 10.0335, 3.2784998, -18.7001, 10.0335, 3.3913999, -16.7235, 6.6889997, 3.3913999, -16.7235, 6.6797, 3.2777998, -18.734999, 10.0335, 3.2784998, -18.7001, 12.7383995, 3.3913999, -15.8953, 12.460499, 3.3913999, -16.1674, 12.747099, 3.1769, -16.1994, 4.6816, 3.3913999, -13.378599, 3.5892, 3.2033, -16.7235, 3.8384998, 3.3913999, -16.7235, 4.6816, 3.3913999, -13.378599, 4.413, 3.2091, -13.357899, 3.5892, 3.2033, -16.7235, -10.6998005, 3.3913999, -13.1763, -6.6896996, 3.175, -12.867498, -6.6896996, 3.3913999, -12.5385, -10.6998005, 3.3913999, -13.1763, -10.4046, 3.1608999, -13.429499, -6.6896996, 3.175, -12.867498, -22.8711, 3.4937, 3.3673, -23.3678, 3.4459999, 0.13709998, -23.171, 3.6251, 0.12290001, -22.8711, 3.4937, 3.3673, -23.079899, 3.314, 3.3673, -23.3678, 3.4459999, 0.13709998, -22.8711, 3.4937, 3.3673, -20.068499, 3.3913999, -0.000100016594, -20.068499, 3.3913999, 3.3442, -22.8711, 3.4937, 3.3673, -23.171, 3.6251, 0.12290001, -20.068499, 3.3913999, -0.000100016594, -23.079899, 3.314, 3.3673, -22.315199, 3.3913999, 6.6891, -22.5639, 3.2219, 6.6891, -23.079899, 3.314, 3.3673, -22.8711, 3.4937, 3.3673, -22.315199, 3.3913999, 6.6891, -22.315199, 3.3913999, 6.6891, -20.068499, 3.3913999, 3.3442, -20.068499, 3.3913999, 6.6891, -22.315199, 3.3913999, 6.6891, -22.8711, 3.4937, 3.3673, -20.068499, 3.3913999, 3.3442, -15.739201, 3.2087, 12.341301, -13.378799, 3.3913999, 10.0334, -13.378799, 3.3922, 12.5714, -15.739201, 3.2087, 12.341301, -16.0544, 3.3913999, 9.808, -13.378799, 3.3913999, 10.0334, -10.034201, 3.2142, 12.105801, -13.378799, 3.1938999, 12.808599, -13.378799, 3.3922, 12.5714, -10.034201, 3.2142, 12.105801, -10.034201, 2.9384, 12.453701, -13.378799, 3.1938999, 12.808599, -13.378799, 3.3922, 12.5714, -10.034201, 3.3913999, 10.0334, -10.034201, 3.2142, 12.105801, -13.378799, 3.3922, 12.5714, -13.378799, 3.3913999, 10.0334, -10.034201, 3.3913999, 10.0334, 0, 3.2243998, 15.7092, -3.3452, 3.1497998, 15.914499, -3.3452, 3.3913999, 15.601, 0, 3.2243998, 15.7092, 0, 2.9636998, 16.0175, -3.3452, 3.1497998, 15.914499, -3.3452, 3.3913999, 15.601, 0, 3.3913999, 13.3783, 0, 3.2243998, 15.7092, -3.3452, 3.3913999, 15.601, -3.3452, 3.3913999, 13.3783, 0, 3.3913999, 13.3783, 0, 3.2243998, 15.7092, 3.3444998, 3.3913999, 13.3783, 3.6397, 3.3913999, 16.2405, 0, 3.2243998, 15.7092, 0, 3.3913999, 13.3783, 3.3444998, 3.3913999, 13.3783, 3.6397, 3.3913999, 16.2405, 3.9143, 3.3913999, 16.5144, 3.6729999, 3.1532, 16.5718, 13.2491, 2.9931, -3.474, 16.722599, 3.3913999, -6.6894, 16.722599, 3.1513, -3.6023998, 13.2491, 2.9931, -3.474, 13.378099, 3.3913999, -6.6894, 16.722599, 3.3913999, -6.6894, 10.0335, 3.3913999, -3.3451, 13.378099, 3.3913999, -6.6894, 13.2491, 2.9931, -3.474, 10.0335, 3.3913999, -3.3451, 10.0335, 3.3913999, -6.6894, 13.378099, 3.3913999, -6.6894, 19.8151, 3.3908, -10.0177, 20.1469, 3.0588999, -6.6894, 19.8317, 3.2891998, -6.6894, 19.8151, 3.3908, -10.0177, 20.188799, 3.1655998, -10.0154, 20.1469, 3.0588999, -6.6894, 19.8317, 3.2891998, -6.6894, 20.139599, 2.779, -3.3616, 19.8317, 3.0491, -3.6023998, 19.8317, 3.2891998, -6.6894, 20.1469, 3.0588999, -6.6894, 20.139599, 2.779, -3.3616, 16.722599, 3.1513, -3.6023998, 19.8317, 3.2891998, -6.6894, 19.8317, 3.0491, -3.6023998, 16.722599, 3.1513, -3.6023998, 16.722599, 3.3913999, -6.6894, 19.8317, 3.2891998, -6.6894, 16.722599, 3.3913999, -6.6894, 19.8151, 3.3908, -10.0177, 19.8317, 3.2891998, -6.6894, 16.722599, 3.3913999, -6.6894, 16.722599, 3.3913999, -10.0343, 19.8151, 3.3908, -10.0177, 20.139599, 2.779, -3.3616, 16.722599, 3.1513, -3.6023998, 19.8317, 3.0491, -3.6023998, 20.139599, 2.779, -3.3616, 16.712599, 2.8734, -3.3451, 16.722599, 3.1513, -3.6023998, 19.767899, 3.1308, -13.4058, 19.7985, 3.3913999, -12.747999, 19.4075, 3.3913999, -13.109999, 19.767899, 3.1308, -13.4058, 20.1582, 3.1318998, -13.041399, 19.7985, 3.3913999, -12.747999, 19.767899, 3.1308, -13.4058, 16.722599, 3.3913999, -13.109999, 16.8123, 3.1578999, -13.4637985, 19.767899, 3.1308, -13.4058, 19.4075, 3.3913999, -13.109999, 16.722599, 3.3913999, -13.109999, 20.188799, 3.1655998, -10.0154, 19.7985, 3.3913999, -12.747999, 20.1582, 3.1318998, -13.041399, 20.188799, 3.1655998, -10.0154, 19.8151, 3.3908, -10.0177, 19.7985, 3.3913999, -12.747999, 16.722599, 3.3913999, -13.109999, 19.8151, 3.3908, -10.0177, 16.722599, 3.3913999, -10.0343, 19.8151, 3.3908, -10.0177, 16.722599, 3.3913999, -13.109999, 19.4075, 3.3913999, -13.109999, 19.8151, 3.3908, -10.0177, 19.4075, 3.3913999, -13.109999, 19.7985, 3.3913999, -12.747999, 16.4952, 3.3913999, -15.5392, 15.828299, 3.1678, -16.6064, 16.7698, 3.1685998, -15.8202, 16.4952, 3.3913999, -15.5392, 15.5536995, 3.3913999, -16.3177, 15.828299, 3.1678, -16.6064, 16.8123, 3.1578999, -13.4637985, 16.4952, 3.3913999, -15.5392, 16.7698, 3.1685998, -15.8202, 16.8123, 3.1578999, -13.4637985, 16.4746, 3.3913999, -13.3993, 16.4952, 3.3913999, -15.5392, 15.828299, 3.1678, -16.6064, 12.7383995, 3.3913999, -15.8953, 12.747099, 3.1769, -16.1994, 15.828299, 3.1678, -16.6064, 15.5536995, 3.3913999, -16.3177, 12.7383995, 3.3913999, -15.8953, 12.4485, 3.2073998, -18.4711, 12.299599, 2.942, -19.2491, 12.726398, 2.9599, -18.824299, 12.4485, 3.2073998, -18.4711, 12.0203, 3.1891, -18.8983, 12.299599, 2.942, -19.2491, 12.4485, 3.2073998, -18.4711, 12.747099, 3.1769, -16.1994, 12.460499, 3.3913999, -16.1674, 12.4485, 3.2073998, -18.4711, 12.726398, 2.9599, -18.824299, 12.747099, 3.1769, -16.1994, 12.299599, 2.942, -19.2491, 10.0335, 3.2784998, -18.7001, 10.0335, 3.0523999, -19.0296, 12.299599, 2.942, -19.2491, 12.0203, 3.1891, -18.8983, 10.0335, 3.2784998, -18.7001, 10.0335, 3.2784998, -18.7001, 12.460499, 3.3913999, -16.1674, 10.0335, 3.3913999, -16.7235, 12.460499, 3.3913999, -16.1674, 10.0335, 3.2784998, -18.7001, 12.0203, 3.1891, -18.8983, 12.460499, 3.3913999, -16.1674, 12.0203, 3.1891, -18.8983, 12.4485, 3.2073998, -18.4711, 5.6212, 3.2835999, -18.3475, 3.9036999, 3.1690998, -17.2696, 5.4681997, 3.0900998, -18.607199, 5.6212, 3.2835999, -18.3475, 4.0333, 3.3667998, -17.0276, 3.9036999, 3.1690998, -17.2696, 4.0333, 3.3667998, -17.0276, 6.6889997, 3.3913999, -16.7235, 3.8384998, 3.3913999, -16.7235, 6.6889997, 3.3913999, -16.7235, 4.0333, 3.3667998, -17.0276, 5.6212, 3.2835999, -18.3475, 6.6889997, 3.3913999, -16.7235, 5.6212, 3.2835999, -18.3475, 6.6797, 3.2777998, -18.734999, 3.8384998, 3.3913999, -16.7235, 3.9036999, 3.1690998, -17.2696, 4.0333, 3.3667998, -17.0276, 3.8384998, 3.3913999, -16.7235, 3.5892, 3.2033, -16.7235, 3.9036999, 3.1690998, -17.2696, 6.6763997, 3.0570998, -19.0278, 5.6212, 3.2835999, -18.3475, 5.4681997, 3.0900998, -18.607199, 6.6763997, 3.0570998, -19.0278, 6.6797, 3.2777998, -18.734999, 5.6212, 3.2835999, -18.3475, -15.2073, 3.5182998, -15.4569, -16.124199, 3.3711998, -15.150499, -15.441299, 3.3151, -15.7533, -15.2073, 3.5182998, -15.4569, -15.9035, 3.5797, -14.867699, -16.124199, 3.3711998, -15.150499, -15.9035, 3.5797, -14.867699, -13.378799, 3.3913999, -13.378599, -16.1309, 3.6227, -13.6241, -13.378799, 3.3913999, -13.378599, -15.9035, 3.5797, -14.867699, -15.2073, 3.5182998, -15.4569, -13.378799, 3.3913999, -13.378599, -15.2073, 3.5182998, -15.4569, -13.3874, 3.3918998, -15.424398, -16.1309, 3.6227, -13.6241, -16.124199, 3.3711998, -15.150499, -15.9035, 3.5797, -14.867699, -16.1309, 3.6227, -13.6241, -16.3755, 3.4225, -13.6283, -16.124199, 3.3711998, -15.150499, -13.385401, 3.1832, -15.7598, -15.2073, 3.5182998, -15.4569, -15.441299, 3.3151, -15.7533, -13.385401, 3.1832, -15.7598, -13.3874, 3.3918998, -15.424398, -15.2073, 3.5182998, -15.4569, -10.749701, 3.1581, -16.1136, -10.6998005, 3.3913999, -15.507799, -11.021601, 3.3913999, -15.7846985, -10.749701, 3.1581, -16.1136, -10.429201, 3.1564, -15.8326, -10.6998005, 3.3913999, -15.507799, -10.749701, 3.1581, -16.1136, -13.3874, 3.3918998, -15.424398, -13.385401, 3.1832, -15.7598, -10.749701, 3.1581, -16.1136, -11.021601, 3.3913999, -15.7846985, -13.3874, 3.3918998, -15.424398, -10.4046, 3.1608999, -13.429499, -10.6998005, 3.3913999, -15.507799, -10.429201, 3.1564, -15.8326, -10.4046, 3.1608999, -13.429499, -10.6998005, 3.3913999, -13.1763, -10.6998005, 3.3913999, -15.507799, -13.3874, 3.3918998, -15.424398, -10.6998005, 3.3913999, -13.1763, -13.378799, 3.3913999, -13.378599, -10.6998005, 3.3913999, -13.1763, -13.3874, 3.3918998, -15.424398, -11.021601, 3.3913999, -15.7846985, -10.6998005, 3.3913999, -13.1763, -11.021601, 3.3913999, -15.7846985, -10.6998005, 3.3913999, -15.507799, -3.9104, 3.3913999, -12.1610985, -4.0188, 3.1604998, -12.820099, -3.6476998, 3.1597998, -12.4415, -3.9104, 3.3913999, -12.1610985, -4.2794, 3.3913999, -12.5385, -4.0188, 3.1604998, -12.820099, -4.0188, 3.1604998, -12.820099, -6.6896996, 3.3913999, -12.5385, -6.6896996, 3.175, -12.867498, -4.0188, 3.1604998, -12.820099, -4.2794, 3.3913999, -12.5385, -6.6896996, 3.3913999, -12.5385, -3.5373998, 3.3913999, -9.750299, -3.6144996, 3.1664, -10.4478, -3.2335, 3.1696, -9.965099, -3.5373998, 3.3913999, -9.750299, -3.9104, 3.3913999, -10.240799, -3.6144996, 3.1664, -10.4478, -3.6144996, 3.1664, -10.4478, -3.9104, 3.3913999, -12.1610985, -3.6476998, 3.1597998, -12.4415, -3.6144996, 3.1664, -10.4478, -3.9104, 3.3913999, -10.240799, -3.9104, 3.3913999, -12.1610985, -0.0046999454, 3.1827998, -9.064699, -3.5373998, 3.3913999, -9.750299, -3.2335, 3.1696, -9.965099, -0.0046999454, 3.1827998, -9.064699, 0, 3.3913999, -8.714399, -3.5373998, 3.3913999, -9.750299, 4.1105, 3.3913999, -9.3386, 3.149, 3.1724, -9.052198, 3.8465, 3.1766, -9.664, 4.1105, 3.3913999, -9.3386, 3.4176, 3.3913999, -8.714399, 3.149, 3.1724, -9.052198, 4.1105, 3.3913999, -9.3386, 4.413, 3.2091, -13.357899, 4.6816, 3.3913999, -13.378599, 4.1105, 3.3913999, -9.3386, 3.8465, 3.1766, -9.664, 4.413, 3.2091, -13.357899, 3.149, 3.1724, -9.052198, 0, 3.3913999, -8.714399, -0.0046999454, 3.1827998, -9.064699, 3.149, 3.1724, -9.052198, 3.4176, 3.3913999, -8.714399, 0, 3.3913999, -8.714399, -19.2812, 3.3913999, -6.6894, -19.611, 3.187, -9.246899, -19.3743, 3.391, -9.417299, -19.2812, 3.3913999, -6.6894, -19.515299, 3.2122, -6.6894, -19.611, 3.187, -9.246899, -16.1255, 3.4196, -10.133099, -16.240599, 3.3848999, -8.961699, -16.7605, 3.3848, -9.501299, -16.1255, 3.4196, -10.133099, -15.5737, 3.3848999, -9.614899, -16.240599, 3.3848999, -8.961699, -16.1255, 3.4196, -10.133099, -17.049099, 3.1696, -9.7829, -16.4075, 3.2108998, -10.402899, -16.1255, 3.4196, -10.133099, -16.7605, 3.3848, -9.501299, -17.049099, 3.1696, -9.7829, -16.7605, 3.3848, -9.501299, -19.1775, 3.1662998, -9.7628, -17.049099, 3.1696, -9.7829, -16.7605, 3.3848, -9.501299, -18.8297, 3.3895998, -9.473499, -19.1775, 3.1662998, -9.7628, -16.3755, 3.4225, -13.6283, -16.1255, 3.4196, -10.133099, -16.4075, 3.2108998, -10.402899, -16.3755, 3.4225, -13.6283, -16.1309, 3.6227, -13.6241, -16.1255, 3.4196, -10.133099, -16.240599, 3.3848999, -8.961699, -13.378799, 3.3913999, -6.6894, -16.723299, 3.3913999, -6.6894, -13.378799, 3.3913999, -6.6894, -16.240599, 3.3848999, -8.961699, -15.5737, 3.3848999, -9.614899, -13.378799, 3.3913999, -6.6894, -15.5737, 3.3848999, -9.614899, -13.378799, 3.3913999, -10.0343, -16.1309, 3.6227, -13.6241, -15.5737, 3.3848999, -9.614899, -16.1255, 3.4196, -10.133099, -15.5737, 3.3848999, -9.614899, -16.1309, 3.6227, -13.6241, -13.378799, 3.3913999, -13.378599, -15.5737, 3.3848999, -9.614899, -13.378799, 3.3913999, -13.378599, -13.378799, 3.3913999, -10.0343, -19.3743, 3.391, -9.417299, -16.723299, 3.3913999, -6.6894, -19.2812, 3.3913999, -6.6894, -16.723299, 3.3913999, -6.6894, -19.3743, 3.391, -9.417299, -16.7605, 3.3848, -9.501299, -16.7605, 3.3848, -9.501299, -19.3743, 3.391, -9.417299, -18.8297, 3.3895998, -9.473499, -16.723299, 3.3913999, -6.6894, -16.7605, 3.3848, -9.501299, -16.240599, 3.3848999, -8.961699, -19.754599, 3.3913999, -2.7854, -19.9515, 3.3913999, -2.5836997, -19.9495, 3.2063, -2.8084998, -19.754599, 3.3913999, -2.7854, -19.515299, 3.2122, -6.6894, -19.2812, 3.3913999, -6.6894, -19.754599, 3.3913999, -2.7854, -19.8424, 3.2077, -3.7668996, -19.515299, 3.2122, -6.6894, -22.7335, 3.5105, -2.4837003, -23.3864, 3.3542, -2.205, -22.931599, 3.3177998, -2.7097, -22.7335, 3.5105, -2.4837003, -23.1883, 3.5481, -1.9755001, -23.3864, 3.3542, -2.205, -23.1883, 3.5481, -1.9755001, -20.068499, 3.3913999, -0.000100016594, -23.171, 3.6251, 0.12290001, -20.068499, 3.3913999, -0.000100016594, -23.1883, 3.5481, -1.9755001, -22.7335, 3.5105, -2.4837003, -20.068499, 3.3913999, -0.000100016594, -22.7335, 3.5105, -2.4837003, -19.9515, 3.3913999, -2.5836997, -23.171, 3.6251, 0.12290001, -23.3864, 3.3542, -2.205, -23.1883, 3.5481, -1.9755001, -23.171, 3.6251, 0.12290001, -23.3678, 3.4459999, 0.13709998, -23.3864, 3.3542, -2.205, -22.7335, 3.5105, -2.4837003, -19.9495, 3.2063, -2.8084998, -19.9515, 3.3913999, -2.5836997, -22.7335, 3.5105, -2.4837003, -22.931599, 3.3177998, -2.7097, -19.9495, 3.2063, -2.8084998, -22.332499, 3.3913999, 8.990399, -22.5912, 3.2112, 9.44, -22.7647, 3.2136, 9.1158, -22.7647, 3.2136, 9.1158, -22.315199, 3.3913999, 6.6891, -22.332499, 3.3913999, 8.990399, -22.7647, 3.2136, 9.1158, -22.5639, 3.2219, 6.6891, -22.315199, 3.3913999, 6.6891, -22.5912, 3.2112, 9.44, -20.068499, 3.3913999, 9.1673, -20.068499, 3.1943, 9.406899, -22.5912, 3.2112, 9.44, -22.332499, 3.3913999, 8.990399, -20.068499, 3.3913999, 9.1673, -6.2755, 3.126, 16.1251, -5.9743, 3.3983, 13.383699, -5.9743, 3.393, 15.6513, -6.2755, 3.126, 16.1251, -6.2755, 3.126, 13.6215, -5.9743, 3.3983, 13.383699, -17.1362, 3.192, 9.7157, -16.285099, 3.3913999, 9.5862, -16.2818, 3.1913998, 9.8050995, -16.285099, 3.3913999, 9.5862, -16.0544, 3.3913999, 9.808, -16.2818, 3.1913998, 9.8050995, -16.285099, 3.3913999, 9.5862, -20.068499, 3.1943, 9.406899, -20.068499, 3.3913999, 9.1673, -16.285099, 3.3913999, 9.5862, -17.1362, 3.192, 9.7157, -20.068499, 3.1943, 9.406899, -16.1122, 3.066, 12.232401, -15.739201, 3.2087, 12.341301, -15.571699, 3.0688, 12.748899, -15.739201, 3.2087, 12.341301, -16.2818, 3.1913998, 9.8050995, -16.0544, 3.3913999, 9.808, -15.739201, 3.2087, 12.341301, -16.1122, 3.066, 12.232401, -16.2818, 3.1913998, 9.8050995, -13.378799, 3.3922, 12.5714, -15.571699, 3.0688, 12.748899, -15.739201, 3.2087, 12.341301, -13.378799, 3.3922, 12.5714, -13.378799, 3.1938999, 12.808599, -15.571699, 3.0688, 12.748899, -6.4743, 3.3766, 12.7861, -5.5162, 3.398, 12.9678, -5.9743, 3.3983, 13.383699, -6.4743, 3.3766, 12.7861, -6.088, 3.3981, 12.428801, -5.5162, 3.398, 12.9678, -6.4743, 3.3766, 12.7861, -6.2755, 3.126, 13.6215, -6.7781997, 3.1009, 13.0322, -6.4743, 3.3766, 12.7861, -5.9743, 3.3983, 13.383699, -6.2755, 3.126, 13.6215, -6.4743, 3.3766, 12.7861, -10.034201, 2.9384, 12.453701, -10.034201, 3.2142, 12.105801, -6.4743, 3.3766, 12.7861, -6.7781997, 3.1009, 13.0322, -10.034201, 2.9384, 12.453701, -10.034201, 3.3913999, 10.0334, -6.4743, 3.3766, 12.7861, -10.034201, 3.2142, 12.105801, -6.4743, 3.3766, 12.7861, -10.034201, 3.3913999, 10.0334, -6.6896996, 3.3913999, 10.0334, -6.4743, 3.3766, 12.7861, -6.6896996, 3.3913999, 10.0334, -6.088, 3.3981, 12.428801, -6.6896996, 3.3913999, 10.0334, -5.5162, 3.398, 12.9678, -6.088, 3.3981, 12.428801, -5.5162, 3.398, 12.9678, -6.6896996, 3.3913999, 10.0334, -3.3452, 3.4796999, 10.0334, -5.5162, 3.398, 12.9678, -3.3452, 3.4796999, 10.0334, -3.3452, 3.3913999, 13.3783, -5.9743, 3.393, 15.6513, -6.0454, 3.135, 16.5322, -6.2755, 3.126, 16.1251, -5.9743, 3.393, 15.6513, -5.5313997, 3.3918, 16.0056, -6.0454, 3.135, 16.5322, -6.0454, 3.135, 16.5322, -3.3452, 3.3913999, 15.601, -3.3452, 3.1497998, 15.914499, -6.0454, 3.135, 16.5322, -5.5313997, 3.3918, 16.0056, -3.3452, 3.3913999, 15.601, -5.9743, 3.3983, 13.383699, -5.5313997, 3.3918, 16.0056, -5.9743, 3.393, 15.6513, -5.5313997, 3.3918, 16.0056, -5.9743, 3.3983, 13.383699, -3.3452, 3.3913999, 13.3783, -3.3452, 3.3913999, 13.3783, -5.9743, 3.3983, 13.383699, -5.5162, 3.398, 12.9678, -5.5313997, 3.3918, 16.0056, -3.3452, 3.3913999, 13.3783, -3.3452, 3.3913999, 15.601, 2.7767, 3.0992, 16.3949, 3.6397, 3.3913999, 16.2405, 3.6729999, 3.1532, 16.5718, 3.6397, 3.3913999, 16.2405, 0, 2.9636998, 16.0175, 0, 3.2243998, 15.7092, 3.6397, 3.3913999, 16.2405, 2.7767, 3.0992, 16.3949, 0, 2.9636998, 16.0175, 3.9987998, 3.4764, 18.6069, 3.9143, 3.3913999, 16.5144, 6.6889997, 3.3913999, 16.7227, 3.8212, 3.3128, 18.7347, 4.941, 3.4643, 19.3458, 4.8006997, 3.3019998, 19.5215, 3.8212, 3.3128, 18.7347, 3.9987998, 3.4764, 18.6069, 4.941, 3.4643, 19.3458, 6.6923, 3.3525, 19.0394, 4.8006997, 3.3019998, 19.5215, 4.941, 3.4643, 19.3458, 6.6923, 3.3525, 19.0394, 6.7794, 3.1271, 19.2435, 4.8006997, 3.3019998, 19.5215, 3.6729999, 3.1532, 16.5718, 3.9987998, 3.4764, 18.6069, 3.8212, 3.3128, 18.7347, 3.6729999, 3.1532, 16.5718, 3.9143, 3.3913999, 16.5144, 3.9987998, 3.4764, 18.6069, 6.6889997, 3.3913999, 16.7227, 4.941, 3.4643, 19.3458, 3.9987998, 3.4764, 18.6069, 6.6923, 3.3525, 19.0394, 4.941, 3.4643, 19.3458, 6.6889997, 3.3913999, 16.7227, 8.6452, 3.3837, 18.872, 9.5275, 3.2163, 18.6602, 8.8241, 3.2010999, 19.0991, 8.6452, 3.3837, 18.872, 9.3605995, 3.3913, 18.4371, 9.5275, 3.2163, 18.6602, 8.6452, 3.3837, 18.872, 6.7794, 3.1271, 19.2435, 6.6923, 3.3525, 19.0394, 8.6452, 3.3837, 18.872, 8.8241, 3.2010999, 19.0991, 6.7794, 3.1271, 19.2435, 9.5275, 3.2163, 18.6602, 10.388599, 3.3911998, 16.7085, 10.590099, 3.2050998, 16.7167, 9.5275, 3.2163, 18.6602, 9.3605995, 3.3913, 18.4371, 10.388599, 3.3911998, 16.7085, 6.6889997, 3.3913999, 16.7227, 8.6452, 3.3837, 18.872, 6.6923, 3.3525, 19.0394, 8.6452, 3.3837, 18.872, 6.6889997, 3.3913999, 16.7227, 10.388599, 3.3911998, 16.7085, 8.6452, 3.3837, 18.872, 10.388599, 3.3911998, 16.7085, 9.3605995, 3.3913, 18.4371, 9.8819, 3.359, 13.619101, 9.838699, 3.359, 12.8258, 10.225699, 3.399, 13.197901, 9.8819, 3.359, 13.619101, 9.4411, 3.359, 13.2192, 9.838699, 3.359, 12.8258, 9.8819, 3.359, 13.619101, 10.462399, 3.2359, 13.4067, 10.122, 3.1906, 13.8256, 9.8819, 3.359, 13.619101, 10.225699, 3.399, 13.197901, 10.462399, 3.2359, 13.4067, 12.575499, 3.4812999, 13.4044, 10.225699, 3.399, 13.197901, 12.125999, 3.6032, 13.197901, 12.575499, 3.4812999, 13.4044, 10.462399, 3.2359, 13.4067, 10.225699, 3.399, 13.197901, 6.6889997, 3.3913999, 13.3783, 10.388599, 3.3911998, 16.7085, 6.6889997, 3.3913999, 16.7227, 10.388599, 3.3911998, 16.7085, 6.6889997, 3.3913999, 13.3783, 9.4411, 3.359, 13.2192, 10.388599, 3.3911998, 16.7085, 9.4411, 3.359, 13.2192, 9.8819, 3.359, 13.619101, 6.6889997, 3.3913999, 10.0334, 9.4411, 3.359, 13.2192, 6.6889997, 3.3913999, 13.3783, 9.4411, 3.359, 13.2192, 6.6889997, 3.3913999, 10.0334, 10.0335, 3.3913999, 10.0334, 9.4411, 3.359, 13.2192, 10.0335, 3.3913999, 10.0334, 9.838699, 3.359, 12.8258, 9.8819, 3.359, 13.619101, 10.590099, 3.2050998, 16.7167, 10.388599, 3.3911998, 16.7085, 9.8819, 3.359, 13.619101, 10.122, 3.1906, 13.8256, 10.590099, 3.2050998, 16.7167, 12.125999, 3.6032, 13.197901, 13.189199, 3.2312999, 12.5394, 13.262999, 3.3165998, 13.403801, 12.125999, 3.6032, 13.197901, 12.9445, 3.6148, 12.325901, 13.189199, 3.2312999, 12.5394, 13.262999, 3.3165998, 13.403801, 12.575499, 3.4812999, 13.4044, 12.125999, 3.6032, 13.197901, 13.189199, 3.2312999, 12.5394, 12.732399, 3.3913999, 10.0334, 12.977798, 3.2031, 10.0163, 13.189199, 3.2312999, 12.5394, 12.9445, 3.6148, 12.325901, 12.732399, 3.3913999, 10.0334, 10.0335, 3.3913999, 10.0334, 10.225699, 3.399, 13.197901, 9.838699, 3.359, 12.8258, 10.225699, 3.399, 13.197901, 10.0335, 3.3913999, 10.0334, 12.9445, 3.6148, 12.325901, 12.9445, 3.6148, 12.325901, 10.0335, 3.3913999, 10.0334, 12.732399, 3.3913999, 10.0334, 10.225699, 3.399, 13.197901, 12.9445, 3.6148, 12.325901, 12.125999, 3.6032, 13.197901, 12.8249, 2.8544, 7.9231997, 12.9499, 3.3256998, 8.668, 11.173199, 3.3332, 6.6548, 10.0335, 3.3913999, 6.6891, 10.0335, 3.3913999, 4.2546997, 11.173199, 3.3332, 6.6548, 12.732399, 3.3913999, 10.0334, 13.175299, 2.4743, 8.0415, 12.977798, 3.2031, 10.0163, 13.175299, 2.4743, 8.0415, 12.732399, 3.3913999, 10.0334, 12.9499, 3.3256998, 8.668, 13.175299, 2.4743, 8.0415, 12.9499, 3.3256998, 8.668, 12.8249, 2.8544, 7.9231997, 10.0335, 3.3913999, 6.6891, 12.732399, 3.3913999, 10.0334, 10.0335, 3.3913999, 10.0334, 12.732399, 3.3913999, 10.0334, 10.0335, 3.3913999, 6.6891, 11.173199, 3.3332, 6.6548, 12.732399, 3.3913999, 10.0334, 11.173199, 3.3332, 6.6548, 12.9499, 3.3256998, 8.668, 16.722599, 3.1513, -3.6023998, 15.810999, 2.9368, -3.3060002, 13.2491, 2.9931, -3.474, 16.722599, 3.1513, -3.6023998, 16.712599, 2.8734, -3.3451, 15.810999, 2.9368, -3.3060002, 10.0335, 3.2212, -2.2310996, 10.0335, 3.3913999, -3.3451, 13.2491, 2.9931, -3.474, -16.7232, 0.6137, 0, -15.754801, 0.6137, 3.5372, -16.7232, 0.6137, 2.9164, -15.754801, 0.6137, 3.5372, -16.7232, 0.6137, 0, -13.378601, 0.6137, 0, -16.7232, 0.6137, 0, -15.803801, 0.6137, -2.7093, -13.378601, 0.6137, -3.3446002, -16.7232, 0.6137, 0, -13.378601, 0.6137, -3.3446002, -13.378601, 0.6137, 0, -13.378601, 0.6137, 0, -13.378601, 0.6137, -3.3446002, -10.033899, 0.6137, -3.3446002, -15.754801, 0.6137, 3.5372, -13.378601, 0.6137, 0, -14.489201, 0.6137, 4.2777, -13.378601, 0.6137, 0, -10.033899, 0.6137, -3.3446002, -10.033899, 0.6137, 0, -14.489201, 0.6137, 4.2777, -13.378601, 0.6137, 0, -10.033899, 0.6137, 0, -10.033899, 0.6137, 0, -10.033899, 0.6137, -3.3446002, -6.6893, 0.6137, -3.3446002, -10.033899, 0.6137, 0, -6.6893, 0.6137, -3.3446002, -6.6893, 0.6137, 0, -6.6893, 0.6137, 0, -6.6893, 0.6137, -3.3446002, -4.0259, 0.6137, -1.6911998, -6.6893, 0.6137, 0, -4.0259, 0.6137, -1.6911998, -3.3446999, 0.6137, 0, -10.033899, 0.6137, 3.3446, -10.033899, 0.6137, 0, -6.6893, 0.6137, 0, -14.489201, 0.6137, 4.2777, -10.033899, 0.6137, 0, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 0, -3.3446999, 0.6137, 0, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 0, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -3.3446999, 0.6137, 0, -3.3446999, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -14.489201, 0.6137, 4.2777, -10.033899, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 6.6893, -10.033899, 0.6137, 6.6893, -13.6772, 0.6137, 5.2616, -14.489201, 0.6137, 4.2777, -13.6772, 0.6137, 5.2616, -10.033899, 0.6137, 6.6893, -12.7477, 0.6137, 6.3669996, -6.6893, 0.6137, 3.3446, -2.5675998, 0.0266, 7.6215, -6.6893, 0.6137, 6.6893, -2.5675998, 0.0266, 7.6215, -6.6893, 0.6137, 3.3446, -3.3446999, 0.6137, 3.3446, -2.5675998, 0.0266, 7.6215, -3.3446999, 0.6137, 3.3446, -2.1193, 0.6137, 5.2736, 10.033899, 0, -20.467, 6.6766996, 3.0570998, -19.0275, 6.6893, 0, -20.1318, 10.033899, 0, -20.467, 10.033899, 3.0523999, -19.0294, 6.6766996, 3.0570998, -19.0275, 3.0140998, 0, -16.7232, 4.4131, 3.2091, -13.357698, 3.3446, -0.3989, -12.9443, 3.0140998, 0, -16.7232, 3.5895, 3.2033, -16.7232, 4.4131, 3.2091, -13.357698, -6.6893, 0, -14.263699, -10.404301, 3.1608999, -13.429499, -9.6168995, 0, -14.127899, -6.6893, 0, -14.263699, -6.6893, 3.175, -12.866999, -10.404301, 3.1608999, -13.429499, -23.412498, -0.2913, 0.26119995, -23.0793, 3.314, 3.3674998, -23.412498, 0, 3.3446, -23.412498, -0.2913, 0.26119995, -23.3676, 3.4459999, 0.1372, -23.0793, 3.314, 3.3674998, -23.412498, 0, 3.3446, -22.563799, 3.2219, 6.6893, -23.8078, 0, 6.6893, -23.412498, 0, 3.3446, -23.0793, 3.314, 3.3674998, -22.563799, 3.2219, 6.6893, -13.378601, 0, 13.378601, -10.033899, 2.9384, 12.453701, -10.033899, 0, 13.378601, -13.378601, 0, 13.378601, -13.378601, 3.194, 12.808901, -10.033899, 2.9384, 12.453701, -3.3446002, 0, 16.7232, 0, 2.9636998, 16.0179, 0, 0, 16.7232, -3.3446002, 0, 16.7232, -3.3446002, 3.1497998, 15.9146, 0, 2.9636998, 16.0179, 21.2349, 0, -6.6893, 20.188799, 3.1655998, -10.014999, 21.8888, 0, -10.033899, 21.2349, 0, -6.6893, 20.147, 3.0588999, -6.6893, 20.188799, 3.1655998, -10.014999, 20.1586, 3.1318998, -13.0413, 20.809599, 0, -13.7309, 21.251299, 0, -13.3043995, 20.1586, 3.1318998, -13.0413, 19.7684, 3.1308, -13.405399, 20.809599, 0, -13.7309, 20.809599, 0, -13.7309, 16.8128, 3.1578999, -13.463299, 18.0241, 0, -14.301899, 20.809599, 0, -13.7309, 19.7684, 3.1308, -13.405399, 16.8128, 3.1578999, -13.463299, 21.8888, 0, -10.033899, 20.1586, 3.1318998, -13.0413, 21.251299, 0, -13.3043995, 21.8888, 0, -10.033899, 20.188799, 3.1655998, -10.014999, 20.1586, 3.1318998, -13.0413, 16.7704, 3.1685998, -15.819698, 16.3113, 0, -16.9629, 17.503, 0, -15.940498, 16.7704, 3.1685998, -15.819698, 15.8286, 3.1678, -16.6059, 16.3113, 0, -16.9629, 16.3113, 0, -16.9629, 12.747099, 3.1769, -16.199, 13.701399, 0, -17.416399, 16.3113, 0, -16.9629, 15.8286, 3.1678, -16.6059, 12.747099, 3.1769, -16.199, 18.0241, 0, -14.301899, 16.7704, 3.1685998, -15.819698, 17.503, 0, -15.940498, 18.0241, 0, -14.301899, 16.8128, 3.1578999, -13.463299, 16.7704, 3.1685998, -15.819698, 12.961399, 0, -20.5828, 12.727098, 2.9599, -18.8239, 12.2999, 2.942, -19.2491, 12.961399, 0, -20.5828, 13.423399, 0, -20.1574, 12.727098, 2.9599, -18.8239, 12.961399, 0, -20.5828, 10.033899, 3.0523999, -19.0294, 10.033899, 0, -20.467, 12.961399, 0, -20.5828, 12.2999, 2.942, -19.2491, 10.033899, 3.0523999, -19.0294, 13.701399, 0, -17.416399, 12.727098, 2.9599, -18.8239, 13.423399, 0, -20.1574, 13.701399, 0, -17.416399, 12.747099, 3.1769, -16.199, 12.727098, 2.9599, -18.8239, 3.2372, 0, -19.3395, 5.4684, 3.0900998, -18.6069, 3.9039, 3.1690998, -17.2691, 3.2372, 0, -19.3395, 4.4523, 0, -20.444399, 5.4684, 3.0900998, -18.6069, 6.6893, 0, -20.1318, 5.4684, 3.0900998, -18.6069, 4.4523, 0, -20.444399, 6.6893, 0, -20.1318, 6.6766996, 3.0570998, -19.0275, 5.4684, 3.0900998, -18.6069, 3.2372, 0, -19.3395, 3.5895, 3.2033, -16.7232, 3.0140998, 0, -16.7232, 3.2372, 0, -19.3395, 3.9039, 3.1690998, -17.2691, 3.5895, 3.2033, -16.7232, -15.441, 3.3151, -15.753099, -16.7232, 0, -16.113699, -16.1493, 0, -16.8284, -15.441, 3.3151, -15.753099, -16.1238, 3.3711998, -15.15, -16.7232, 0, -16.113699, -16.7232, 0, -16.113699, -16.3753, 3.4225, -13.627699, -16.7232, 0, -13.378599, -16.7232, 0, -16.113699, -16.1238, 3.3711998, -15.15, -16.3753, 3.4225, -13.627699, -13.378601, 0, -17.3364, -15.441, 3.3151, -15.753099, -16.1493, 0, -16.8284, -13.378601, 0, -17.3364, -13.3848, 3.1832, -15.7598, -15.441, 3.3151, -15.753099, -10.3452, 0, -16.7803, -10.4286995, 3.1564, -15.832399, -10.7493, 3.1581, -16.1132, -10.3452, 0, -16.7803, -9.983101, 0, -16.4067, -10.4286995, 3.1564, -15.832399, -10.3452, 0, -16.7803, -13.3848, 3.1832, -15.7598, -13.378601, 0, -17.3364, -10.3452, 0, -16.7803, -10.7493, 3.1581, -16.1132, -13.3848, 3.1832, -15.7598, -9.6168995, 0, -14.127899, -10.4286995, 3.1564, -15.832399, -9.983101, 0, -16.4067, -9.6168995, 0, -14.127899, -10.404301, 3.1608999, -13.429499, -10.4286995, 3.1564, -15.832399, -3.6946998, 0, -13.471199, -3.6475997, 3.1597998, -12.441198, -4.0183997, 3.1606, -12.819899, -3.6946998, 0, -13.471199, -3.2535, 0, -13.020899, -3.6475997, 3.1597998, -12.441198, -3.6946998, 0, -13.471199, -6.6893, 3.175, -12.866999, -6.6893, 0, -14.263699, -3.6946998, 0, -13.471199, -4.0183997, 3.1606, -12.819899, -6.6893, 3.175, -12.866999, -3.2331, 3.1696, -9.964899, -2.7220998, 0, -10.936998, -2.2352996, 0, -10.6031, -3.2331, 3.1696, -9.964899, -3.6138997, 3.1664, -10.447599, -2.7220998, 0, -10.936998, 0, 0, -10.7654, -3.2331, 3.1696, -9.964899, -2.2352996, 0, -10.6031, 0, 0, -10.7654, -0.0039999485, 3.1827998, -9.0644, -3.2331, 3.1696, -9.964899, -2.7220998, 0, -10.936998, -3.6475997, 3.1597998, -12.441198, -3.2535, 0, -13.020899, -2.7220998, 0, -10.936998, -3.6138997, 3.1664, -10.447599, -3.6475997, 3.1597998, -12.441198, 3.847, 3.1766, -9.663599, 2.2182999, 0, -10.525, 2.9573, -0.1021, -11.0952, 3.847, 3.1766, -9.663599, 3.1496, 3.1724, -9.052, 2.2182999, 0, -10.525, 3.3446, -0.3989, -12.9443, 3.847, 3.1766, -9.663599, 2.9573, -0.1021, -11.0952, 3.3446, -0.3989, -12.9443, 4.4131, 3.2091, -13.357698, 3.847, 3.1766, -9.663599, 2.2182999, 0, -10.525, -0.0039999485, 3.1827998, -9.0644, 0, 0, -10.7654, 2.2182999, 0, -10.525, 3.1496, 3.1724, -9.052, -0.0039999485, 3.1827998, -9.0644, -20.108599, 0, -9.592199, -19.176899, 3.1662998, -9.762199, -19.6161, 3.2075999, -9.711699, -20.108599, 0, -9.592199, -19.6286, 0, -10.1098995, -19.176899, 3.1662998, -9.762199, -19.6161, 3.2075999, -9.711699, -20.3764, 0, -6.6893, -20.108599, 0, -9.592199, -20.3764, 0, -6.6893, -19.6161, 3.2075999, -9.711699, -19.610699, 3.187, -9.246599, -20.3764, 0, -6.6893, -19.610699, 3.187, -9.246599, -19.515, 3.2122, -6.6893, -16.407099, 3.2108998, -10.4028, -17.8929, 0, -10.41, -17.1586, 0, -11.120298, -16.407099, 3.2108998, -10.4028, -17.0486, 3.1696, -9.782799, -17.8929, 0, -10.41, -16.7232, 0, -13.378599, -16.407099, 3.2108998, -10.4028, -17.1586, 0, -11.120298, -16.7232, 0, -13.378599, -16.3753, 3.4225, -13.627699, -16.407099, 3.2108998, -10.4028, -17.8929, 0, -10.41, -19.176899, 3.1662998, -9.762199, -19.6286, 0, -10.1098995, -17.8929, 0, -10.41, -17.0486, 3.1696, -9.782799, -19.176899, 3.1662998, -9.762199, -19.841799, 3.2077, -3.7668, -19.7545, 3.3913999, -2.7851996, -19.9489, 3.2063, -2.8082, -19.841799, 3.2077, -3.7668, -21.0086, -0.2504, -3.5043998, -20.1607, -0.2436, -4.5060997, -19.841799, 3.2077, -3.7668, -19.9489, 3.2063, -2.8082, -21.0086, -0.2504, -3.5043998, -20.3764, 0, -6.6893, -19.841799, 3.2077, -3.7668, -20.1607, -0.2436, -4.5060997, -20.3764, 0, -6.6893, -19.515, 3.2122, -6.6893, -19.841799, 3.2077, -3.7668, -22.9314, 3.3177998, -2.7092, -23.412498, -0.0385, -2.8684, -22.9552, -0.047599997, -3.375, -22.9314, 3.3177998, -2.7092, -23.3864, 3.3542, -2.2045999, -23.412498, -0.0385, -2.8684, -23.412498, -0.0385, -2.8684, -23.3676, 3.4459999, 0.1372, -23.412498, -0.2913, 0.26119995, -23.412498, -0.0385, -2.8684, -23.3864, 3.3542, -2.2045999, -23.3676, 3.4459999, 0.1372, -21.0086, -0.2504, -3.5043998, -22.9314, 3.3177998, -2.7092, -22.9552, -0.047599997, -3.375, -21.0086, -0.2504, -3.5043998, -19.9489, 3.2063, -2.8082, -22.9314, 3.3177998, -2.7092, -22.756899, 0, 10.033899, -22.7643, 3.2136, 9.1159, -22.5907, 3.2112, 9.440499, -22.756899, 0, 10.033899, -23.489, 0, 9.386499, -22.7643, 3.2136, 9.1159, -23.8078, 0, 6.6893, -22.7643, 3.2136, 9.1159, -23.489, 0, 9.386499, -23.8078, 0, 6.6893, -22.563799, 3.2219, 6.6893, -22.7643, 3.2136, 9.1159, -16.7232, 0, 10.8878, -17.1359, 3.192, 9.715799, -16.2818, 3.1913998, 9.8056, -16.7232, 0, 10.8878, -17.583, 0, 10.033899, -17.1359, 3.192, 9.715799, -20.0679, 0, 10.033899, -17.1359, 3.192, 9.715799, -17.583, 0, 10.033899, -20.0679, 0, 10.033899, -20.0679, 3.1943, 9.407499, -17.1359, 3.192, 9.715799, -16.111599, 3.066, 12.2328, -16.223, 0, 13.378601, -16.7232, 0, 12.878401, -16.111599, 3.066, 12.2328, -15.5713005, 3.0688999, 12.748899, -16.223, 0, 13.378601, -16.223, 0, 13.378601, -13.378601, 3.194, 12.808901, -13.378601, 0, 13.378601, -16.223, 0, 13.378601, -15.5713005, 3.0688999, 12.748899, -13.378601, 3.194, 12.808901, -16.7232, 0, 10.8878, -16.111599, 3.066, 12.2328, -16.7232, 0, 12.878401, -16.7232, 0, 10.8878, -16.2818, 3.1913998, 9.8056, -16.111599, 3.066, 12.2328, -6.6893, 0, 13.8773, -6.7777, 3.1009, 13.0326, -6.2748, 3.126, 13.622, -6.6893, 0, 13.8773, -7.1966996, 0, 13.378601, -6.7777, 3.1009, 13.0326, -10.033899, 0, 13.378601, -6.7777, 3.1009, 13.0326, -7.1966996, 0, 13.378601, -10.033899, 0, 13.378601, -10.033899, 2.9384, 12.453701, -6.7777, 3.1009, 13.0326, -6.1093993, 0, 16.7232, -6.2748, 3.1260998, 16.1257, -6.0452, 3.135, 16.5325, -6.1093993, 0, 16.7232, -6.6893, 0, 16.1455, -6.2748, 3.1260998, 16.1257, -6.1093993, 0, 16.7232, -3.3446002, 3.1497998, 15.9146, -3.3446002, 0, 16.7232, -6.1093993, 0, 16.7232, -6.0452, 3.135, 16.5325, -3.3446002, 3.1497998, 15.9146, -6.6893, 0, 13.8773, -6.2748, 3.1260998, 16.1257, -6.6893, 0, 16.1455, -6.6893, 0, 13.8773, -6.2748, 3.126, 13.622, -6.2748, 3.1260998, 16.1257, 3.3446, 0, 17.592, 2.777, 3.0992, 16.3951, 3.6736, 3.1532, 16.5721, 3.3446, 0, 17.592, 2.4738998, 0, 16.7232, 2.777, 3.0992, 16.3951, 0, 0, 16.7232, 2.777, 3.0992, 16.3951, 2.4738998, 0, 16.7232, 0, 0, 16.7232, 0, 2.9636998, 16.0179, 2.777, 3.0992, 16.3951, 3.3446, 0, 17.592, 3.8212998, 3.3128, 18.735199, 3.3446, 0, 19.1425, 3.3446, 0, 17.592, 3.6736, 3.1532, 16.5721, 3.8212998, 3.3128, 18.735199, 4.275, 0, 20.0679, 6.7795997, 3.1271, 19.243599, 6.6893, 0, 20.0679, 4.275, 0, 20.0679, 4.8012, 3.3019998, 19.5215, 6.7795997, 3.1271, 19.243599, 3.8212998, 3.3128, 18.735199, 4.275, 0, 20.0679, 3.3446, 0, 19.1425, 3.8212998, 3.3128, 18.735199, 4.8012, 3.3019998, 19.5215, 4.275, 0, 20.0679, -22.756899, 0, 10.033899, -20.0679, 3.1943, 9.407499, -20.0679, 0, 10.033899, -22.756899, 0, 10.033899, -22.5907, 3.2112, 9.440499, -20.0679, 3.1943, 9.407499, 10.572599, 0, 19.5895, 8.8245, 3.2012, 19.0993, 9.5278, 3.2163, 18.6607, 10.572599, 0, 19.5895, 10.080299, 0, 20.0679, 8.8245, 3.2012, 19.0993, 6.6893, 0, 20.0679, 8.8245, 3.2012, 19.0993, 10.080299, 0, 20.0679, 6.6893, 0, 20.0679, 6.7795997, 3.1271, 19.243599, 8.8245, 3.2012, 19.0993, 10.572599, 0, 19.5895, 10.5903, 3.2052, 16.7169, 10.572599, 0, 16.7232, 10.572599, 0, 19.5895, 9.5278, 3.2163, 18.6607, 10.5903, 3.2052, 16.7169, 10.986898, 0, 13.9188, 10.122399, 3.1906, 13.826099, 10.462799, 3.2359, 13.406799, 10.986898, 0, 13.9188, 10.572599, 0, 14.3402, 10.122399, 3.1906, 13.826099, 10.572599, 0, 16.7232, 10.122399, 3.1906, 13.826099, 10.572599, 0, 14.3402, 10.572599, 0, 16.7232, 10.5903, 3.2052, 16.7169, 10.122399, 3.1906, 13.826099, 13.8046, 0, 13.2202, 12.5756, 3.4812999, 13.4049, 13.2631, 3.3165998, 13.404301, 13.8046, 0, 13.2202, 13.105099, 0, 13.9188, 12.5756, 3.4812999, 13.4049, 13.2631, 3.3165998, 13.404301, 13.8046, 0, 10.033899, 13.8046, 0, 13.2202, 13.8046, 0, 10.033899, 13.2631, 3.3165998, 13.404301, 13.1894, 3.2312999, 12.539801, 13.8046, 0, 10.033899, 13.1894, 3.2312999, 12.539801, 12.9783, 3.2031, 10.0168, 10.986898, 0, 13.9188, 12.5756, 3.4812999, 13.4049, 13.105099, 0, 13.9188, 10.986898, 0, 13.9188, 10.462799, 3.2359, 13.406799, 12.5756, 3.4812999, 13.4049, 13.8046, 0, 10.033899, 13.175798, 2.4743, 8.042, 14.0257, 0.0442, 7.3252997, 13.8046, 0, 10.033899, 12.9783, 3.2031, 10.0168, 13.175798, 2.4743, 8.042, 20.1399, 2.779, -3.3615003, 21.09, -0.0218, -4.9179, 20.613499, 1.0832, -3.1922002, 21.09, -0.0218, -4.9179, 20.1399, 2.779, -3.3615003, 20.147, 3.0588999, -6.6893, 21.09, -0.0218, -4.9179, 20.147, 3.0588999, -6.6893, 21.2349, 0, -6.6893, 15.6619, 2.2603, -2.7998996, 20.1399, 2.779, -3.3615003, 20.613499, 1.0832, -3.1922002, 20.1399, 2.779, -3.3615003, 15.6619, 2.2603, -2.7998996, 16.7127, 2.8734, -3.3446002, 16.7127, 2.8734, -3.3446002, 15.6619, 2.2603, -2.7998996, 15.810999, 2.9368, -3.3059, 13.249599, 2.9931998, -3.4737, 15.810999, 2.9368, -3.3059, 14.217899, 2.8779, -3.3838, 14.217899, 2.8779, -3.3838, 15.810999, 2.9368, -3.3059, 15.6619, 2.2603, -2.7998996, -16.7232, 3.0033998, 2.9164, -15.754801, 0.6137, 3.5372, -15.372101, 3.0033998, 3.6132, -16.7232, 3.0033998, 2.9164, -16.7232, 0.6137, 2.9164, -15.754801, 0.6137, 3.5372, -14.489201, 3.0033998, 4.2777, -13.6772, 0.6137, 5.2616, -13.694401, 3.0033998, 5.1798, -14.489201, 3.0033998, 4.2777, -14.489201, 0.6137, 4.2777, -13.6772, 0.6137, 5.2616, -15.803801, 3.0033998, -2.7093, -16.7232, 0.6137, 0, -16.7232, 3.0033998, 0, -15.803801, 3.0033998, -2.7093, -15.803801, 0.6137, -2.7093, -16.7232, 0.6137, 0, -16.7232, 3.0033998, 0, -16.7232, 0.6137, 2.9164, -16.7232, 3.0033998, 2.9164, -16.7232, 3.0033998, 0, -16.7232, 0.6137, 0, -16.7232, 0.6137, 2.9164, -3.3446999, 3.0033998, 0, -4.0259, 0.6137, -1.6911998, -4.0259, 3.0033998, -1.6911998, -3.3446999, 3.0033998, 0, -3.3446999, 0.6137, 0, -4.0259, 0.6137, -1.6911998, -3.3446999, 3.0033998, 3.3446, -3.3446999, 0.6137, 0, -3.3446999, 3.0033998, 0, -3.3446999, 3.0033998, 3.3446, -3.3446999, 0.6137, 3.3446, -3.3446999, 0.6137, 0, -12.7477, 3.0033998, 6.3669996, -10.033899, 0.6137, 6.6893, -10.033899, 3.0033998, 6.6893, -12.7477, 3.0033998, 6.3669996, -12.7477, 0.6137, 6.3669996, -10.033899, 0.6137, 6.6893, -10.033899, 3.0033998, -3.3446002, -13.378601, 0.6137, -3.3446002, -13.378601, 3.0033998, -3.3446002, -10.033899, 3.0033998, -3.3446002, -10.033899, 0.6137, -3.3446002, -13.378601, 0.6137, -3.3446002, -6.6893, 3.0033998, -3.3446002, -10.033899, 0.6137, -3.3446002, -10.033899, 3.0033998, -3.3446002, -6.6893, 3.0033998, -3.3446002, -6.6893, 0.6137, -3.3446002, -10.033899, 0.6137, -3.3446002, -13.378601, 3.0033998, -3.3446002, -15.803801, 0.6137, -2.7093, -15.803801, 3.0033998, -2.7093, -13.378601, 3.0033998, -3.3446002, -13.378601, 0.6137, -3.3446002, -15.803801, 0.6137, -2.7093, -4.0259, 3.0033998, -1.6911998, -6.6893, 0.6137, -3.3446002, -6.6893, 3.0033998, -3.3446002, -4.0259, 3.0033998, -1.6911998, -4.0259, 0.6137, -1.6911998, -6.6893, 0.6137, -3.3446002, -6.6893, 3.0033998, 6.6893, -2.5675998, 0.0266, 7.6215, -2.5675998, 2.9782, 7.6215, -6.6893, 3.0033998, 6.6893, -6.6893, 0.6137, 6.6893, -2.5675998, 0.0266, 7.6215, -10.033899, 3.0033998, 6.6893, -6.6893, 0.6137, 6.6893, -6.6893, 3.0033998, 6.6893, -10.033899, 3.0033998, 6.6893, -10.033899, 0.6137, 6.6893, -6.6893, 0.6137, 6.6893, -2.5675998, 2.9782, 7.6215, -2.1193, 0.6137, 5.2736, -2.1397996, 3.0033998, 5.1794, -2.5675998, 2.9782, 7.6215, -2.5675998, 0.0266, 7.6215, -2.1193, 0.6137, 5.2736, -2.1193, 0.6137, 5.2736, -3.3446999, 3.0033998, 3.3446, -2.1397996, 3.0033998, 5.1794, -2.1193, 0.6137, 5.2736, -3.3446999, 0.6137, 3.3446, -3.3446999, 3.0033998, 3.3446, -13.6772, 0.6137, 5.2616, -12.7477, 3.0033998, 6.3669996, -13.694401, 3.0033998, 5.1798, -13.6772, 0.6137, 5.2616, -12.7477, 0.6137, 6.3669996, -12.7477, 3.0033998, 6.3669996, -15.754801, 0.6137, 3.5372, -14.489201, 3.0033998, 4.2777, -15.372101, 3.0033998, 3.6132, -15.754801, 0.6137, 3.5372, -14.489201, 0.6137, 4.2777, -14.489201, 3.0033998, 4.2777, -3.3446002, -1.4008, -23.412498, 0, -1.0608, -26.7572, 0, -0.9271, -23.412498, -3.3446002, -1.4008, -23.412498, -3.3446002, -1.458, -26.7572, 0, -1.0608, -26.7572, -16.7232, -1.7572, -23.412498, -13.378601, -2.7663999, -26.7572, -13.378601, -1.3177999, -23.412498, -16.7232, -1.7572, -23.412498, -16.7232, -3.0929, -26.7572, -13.378601, -2.7663999, -26.7572, 10.033899, 0, -23.412498, 13.378599, -0.8401, -26.7572, 13.378599, 0, -23.412498, 10.033899, 0, -23.412498, 10.033899, -0.8648, -26.7572, 13.378599, -0.8401, -26.7572, 3.3446, -0.3434, -23.412498, 6.6893, -1.0129, -26.7572, 6.6893, 0, -23.412498, 3.3446, -0.3434, -23.412498, 3.3446, -0.98829997, -26.7572, 6.6893, -1.0129, -26.7572, -23.412498, -2.7663999, -23.412498, -20.0679, -3.0929, -26.7572, -20.0679, -2.7663999, -23.412498, -23.412498, -2.7663999, -23.412498, -23.412498, -3.0929, -26.7572, -20.0679, -3.0929, -26.7572, -26.7572, -2.7663999, -23.412498, -23.412498, -3.0929, -26.7572, -23.412498, -2.7663999, -23.412498, -26.7572, -2.7663999, -23.412498, -26.7572, -3.0929, -26.7572, -23.412498, -3.0929, -26.7572, -23.412498, -0.7606, -16.7232, -20.0679, -1.7572, -20.0679, -20.0679, -0.43429998, -16.7232, -23.412498, -0.7606, -16.7232, -23.412498, -2.7663999, -20.0679, -20.0679, -1.7572, -20.0679, -10.033899, -0.2814, 23.412498, -6.6893, 0, 20.0679, -6.6893, -0.2814, 23.412498, -6.6893, -0.2814, 23.412498, -6.6893, 0, 20.0679, -3.3446002, 0, 20.0679, -10.033899, -0.2814, 23.412498, -10.033899, 0, 20.0679, -6.6893, 0, 20.0679, -6.6893, -0.2814, 23.412498, -3.3446002, 0, 20.0679, -3.3446002, -0.2814, 23.412498, -3.3446002, -0.2814, 23.412498, -3.3446002, 0, 20.0679, 0, 0, 20.0679, -3.3446002, -0.2814, 23.412498, 0, 0, 20.0679, 0, -0.2814, 23.412498, -13.378601, 0, 20.0679, -10.033899, 0, 20.0679, -10.033899, -0.2814, 23.412498, -13.378601, 0, 20.0679, -10.033899, -0.2814, 23.412498, -12.357901, -0.2814, 23.412498, -13.378601, -0.2814, 23.412498, -13.378601, 0, 20.0679, -12.357901, -0.2814, 23.412498, -23.412498, -0.36479998, 16.7232, -23.412498, 0, 13.378601, -20.0679, -0.36479998, 16.7232, -23.412498, 0, 13.378601, -20.0679, 0, 13.378601, -20.0679, -0.36479998, 16.7232, -23.412498, -0.7373, 23.412498, -23.412498, -0.692, 20.0679, -20.0679, -0.5589, 23.412498, -16.7232, -0.36479998, 23.412498, -16.7232, -0.36479998, 20.0679, -13.378601, -0.2814, 23.412498, 16.7232, -1.0746, 16.7232, 20.0679, -1.0746, 13.378601, 20.0679, -1.8294, 16.7232, 20.0679, -1.8294, 16.7232, 20.0679, -1.0746, 13.378601, 23.412498, -1.8294, 13.378601, 16.7232, -1.8294, 20.0679, 16.7232, -1.0746, 16.7232, 20.0679, -1.8294, 16.7232, 16.7232, -2.2719998, 23.3902, 20.0679, -1.8294, 20.0679, 20.0679, -2.2719998, 23.3902, 16.7232, -2.2719998, 23.3902, 16.7232, -1.8294, 20.0679, 20.0679, -1.8294, 20.0679, 16.7232, -1.8294, 20.0679, 16.7232, -2.2719998, 23.3902, 13.378599, -2.2719998, 23.3902, 20.0679, -2.2719998, 23.3902, 20.0679, -1.8294, 20.0679, 23.412498, -1.8294, 20.0679, 20.0679, -2.2719998, 23.3902, 23.412498, -1.8294, 20.0679, 23.412498, -2.2719998, 23.3902, 23.412498, -1.8294, 20.0679, 26.7582, -2.2719998, 23.3902, 23.412498, -2.2719998, 23.3902, 23.412498, -1.8294, 20.0679, 26.7582, -1.8294, 20.0679, 26.7582, -2.2719998, 23.3902, -10.033899, -0.9271, -23.412498, -6.6893, -1.458, -26.7572, -6.6893, -1.3241999, -23.412498, -10.033899, -0.9271, -23.412498, -10.033899, -1.8786, -26.7572, -6.6893, -1.458, -26.7572, -3.3446002, -0.9271, -16.7232, 0, -0.9271, -20.0679, 0, -0.9271, -16.7232, -3.3446002, -0.9271, -16.7232, -3.3446002, -0.9271, -20.0679, 0, -0.9271, -20.0679, -6.6893, -0.9271, -20.0679, -3.3446002, -0.9271, -20.0679, -3.3446002, -0.9271, -16.7232, 16.7232, -0.3105, -23.412498, 16.7232, -1.3382, -26.7572, 20.0679, -0.8222, -23.412498, 20.0679, 0, -16.7232, 20.0679, -0.2837, -20.0679, 23.412498, -0.456, -16.7232, 13.378599, 0, -23.412498, 13.378599, -0.8401, -26.7572, 16.7232, -0.3105, -23.412498, 16.7232, 0, -20.0679, 16.7232, -0.3105, -23.412498, 20.0679, -0.2837, -20.0679, -6.6893, 0, -16.7232, -6.6893, -0.9271, -20.0679, -3.3446002, -0.9271, -16.7232, -13.378601, -1.3177999, -23.412498, -10.033899, -1.8786, -26.7572, -10.033899, -0.9271, -23.412498, -13.378601, -1.3177999, -23.412498, -13.378601, -2.7663999, -26.7572, -10.033899, -1.8786, -26.7572, -13.210501, -0.3964, -20.032099, -10.033899, -0.9271, -23.412498, -9.318001, -0.3969, -19.413, -13.210501, -0.3964, -20.032099, -13.378601, -1.3177999, -23.412498, -10.033899, -0.9271, -23.412498, -9.318001, -0.3969, -19.413, -10.033899, -0.9271, -23.412498, -6.6893, -0.9271, -20.0679, 20.0679, -1.0746, 13.378601, 20.0679, 0, 10.033899, 23.412498, -0.35529998, 10.033899, 13.378599, -2.2719998, 23.3902, 16.7232, -2.2194998, 26.781, 13.378599, -2.2194998, 26.781, 13.378599, -2.2719998, 23.3902, 16.7232, -2.2719998, 23.3902, 16.7232, -2.2194998, 26.781, 16.7232, -2.2194998, 26.781, 16.7232, -2.2719998, 23.3902, 20.0679, -2.2719998, 23.3902, 16.7232, -2.2194998, 26.781, 20.0679, -2.2719998, 23.3902, 20.0679, -2.2194998, 26.781, 20.0679, -2.2194998, 26.781, 20.0679, -2.2719998, 23.3902, 23.412498, -2.2719998, 23.3902, 20.0679, -2.2194998, 26.781, 23.412498, -2.2719998, 23.3902, 23.412498, -2.2194998, 26.781, 13.378599, -1.0746, 20.0679, 15.0508995, -0.40039998, 16.7232, 16.7232, -1.0746, 16.7232, 16.7232, -1.0746, 13.378601, 16.7232, 0, 10.033899, 20.0679, 0, 10.033899, 20.0679, 0, 10.033899, 20.0679, -1.0746, 13.378601, 16.7232, -1.0746, 13.378601, 6.6893, -1.1209999, 26.7572, 10.033899, -0.501, 23.412498, 10.033899, -1.1209999, 26.7572, 6.6893, -1.1209999, 26.7572, 6.6893, -0.2814, 23.412498, 10.033899, -0.501, 23.412498, 10.033899, -1.1209999, 26.7572, 13.378599, -2.2719998, 23.3902, 13.378599, -2.2194998, 26.781, 10.033899, -1.1209999, 26.7572, 10.033899, -0.501, 23.412498, 13.378599, -2.2719998, 23.3902, 0, -1.1209999, 26.7572, 3.3446, -0.2814, 23.412498, 3.3446, -1.1209999, 26.7572, 3.3446, -1.1209999, 26.7572, 3.3446, -0.2814, 23.412498, 6.6893, -0.2814, 23.412498, 3.3446, -1.1209999, 26.7572, 6.6893, -0.2814, 23.412498, 6.6893, -1.1209999, 26.7572, 0, -1.1209999, 26.7572, 0, -0.2814, 23.412498, 3.3446, -0.2814, 23.412498, -3.3446002, -1.1209999, 26.7572, 0, -0.2814, 23.412498, 0, -1.1209999, 26.7572, -3.3446002, -1.1209999, 26.7572, -3.3446002, -0.2814, 23.412498, 0, -0.2814, 23.412498, -6.6893, -1.1209999, 26.7572, -3.3446002, -0.2814, 23.412498, -3.3446002, -1.1209999, 26.7572, -6.6893, -1.1209999, 26.7572, -6.6893, -0.2814, 23.412498, -3.3446002, -0.2814, 23.412498, -10.033899, -1.1209999, 26.7572, -6.6893, -0.2814, 23.412498, -6.6893, -1.1209999, 26.7572, -10.033899, -1.1209999, 26.7572, -10.033899, -0.2814, 23.412498, -6.6893, -0.2814, 23.412498, -10.033899, -1.1209999, 26.7572, -12.357901, -0.2814, 23.412498, -10.033899, -0.2814, 23.412498, -10.033899, -1.1209999, 26.7572, -13.378601, -0.2814, 23.412498, -12.357901, -0.2814, 23.412498, -13.378601, -0.2814, 23.412498, -10.033899, -1.1209999, 26.7572, -13.378601, -1.1209999, 26.7572, 16.7232, -1.0746, 13.378601, 15.0508995, -0.6288, 13.378601, 16.7232, 0, 10.033899, -29.294899, -0.6617, 3.3446, -25.479, -0.4588, 0.143, -25.869999, -0.2764, 3.3446, -29.294899, -0.6617, 3.3446, -27.9812, -0.6617, 0, -25.479, -0.4588, 0.143, -29.294899, -0.6617, 6.6893, -25.869999, -0.2764, 3.3446, -25.945799, -0.25779998, 6.6893, -29.294899, -0.6617, 6.6893, -29.294899, -0.6617, 3.3446, -25.869999, -0.2764, 3.3446, -20.0679, -0.5589, 23.412498, -20.0679, -0.36479998, 20.0679, -16.7232, -0.36479998, 23.412498, -20.0679, -1.1374999, 26.7572, -20.0679, -0.5589, 23.412498, -16.7232, -1.1374999, 26.7572, -16.7232, -1.1374999, 26.7572, -16.7232, -0.36479998, 23.412498, -13.378601, -1.1209999, 26.7572, -26.7572, -1.0756, 23.412498, -25.174, -0.7391, 20.0679, -23.412498, -0.7373, 23.412498, -26.7572, -1.0756, 23.412498, -26.7572, -1.0756, 20.0679, -25.174, -0.7391, 20.0679, -26.7572, -1.2701, 26.7572, -23.412498, -0.7373, 23.412498, -23.412498, -1.1374999, 26.7572, -26.7572, -1.2701, 26.7572, -26.7572, -1.0756, 23.412498, -23.412498, -0.7373, 23.412498, -23.412498, -1.1374999, 26.7572, -23.412498, -0.7373, 23.412498, -20.0679, -1.1374999, 26.7572, -29.294899, -1.0265, 16.7232, -25.9681, -0.2875, 13.378601, -25.4212, -0.5907, 16.7232, -29.294899, -1.0265, 16.7232, -29.294899, -0.6617, 13.378601, -25.9681, -0.2875, 13.378601, -25.9681, -0.2875, 13.378601, -23.412498, -0.36479998, 16.7232, -25.4212, -0.5907, 16.7232, -23.412498, -0.36479998, 16.7232, -25.9681, -0.2875, 13.378601, -23.412498, 0, 13.378601, -26.7572, -1.0756, 20.0679, -25.4212, -0.5907, 16.7232, -25.174, -0.7391, 20.0679, -26.7572, -1.0756, 20.0679, -29.294899, -1.0265, 16.7232, -25.4212, -0.5907, 16.7232, -23.412498, -0.692, 20.0679, -23.412498, -0.36479998, 16.7232, -20.0679, -0.36479998, 20.0679, -13.378601, 0, 16.7232, -10.033899, 0, 13.378601, -10.033899, 0, 16.7232, -13.378601, 0, 16.7232, -13.378601, 0, 13.378601, -10.033899, 0, 13.378601, -13.378601, 0, 20.0679, -13.378601, 0, 16.7232, -10.033899, 0, 16.7232, -13.378601, 0, 20.0679, -10.033899, 0, 16.7232, -10.033899, 0, 20.0679, -10.033899, 0, 16.7232, -6.6893, 0, 20.0679, -10.033899, 0, 20.0679, -10.033899, 0, 13.378601, -7.2641997, 0, 16.7232, -10.033899, 0, 16.7232, -6.6893, 0, 20.0679, -10.033899, 0, 16.7232, -7.2641997, 0, 16.7232, -6.6893, 0, 13.8773, -10.033899, 0, 13.378601, -7.1966996, 0, 13.378601, -7.2641997, 0, 16.7232, -10.033899, 0, 13.378601, -6.6893, 0, 13.8773, -7.2641997, 0, 16.7232, -6.6893, 0, 13.8773, -6.6893, 0, 16.1455, -6.1093993, 0, 16.7232, -7.2641997, 0, 16.7232, -6.6893, 0, 16.1455, -6.6893, 0, 20.0679, -7.2641997, 0, 16.7232, -6.6893, 0, 17.298199, -6.1093993, 0, 16.7232, -6.6893, 0, 17.298199, -7.2641997, 0, 16.7232, -6.6893, 0, 17.298199, -3.3446002, 0, 20.0679, -6.6893, 0, 20.0679, -3.3446002, 0, 20.0679, -6.6893, 0, 17.298199, -6.1093993, 0, 16.7232, -3.3446002, 0, 20.0679, -6.1093993, 0, 16.7232, -3.3446002, 0, 16.7232, -3.3446002, 0, 20.0679, -3.3446002, 0, 16.7232, 0, 0, 16.7232, -3.3446002, 0, 20.0679, 0, 0, 16.7232, 0, 0, 20.0679, 0, 0, 16.7232, 2.4261, 0, 20.0679, 0, 0, 20.0679, 2.4261, 0, 20.0679, 0, 0, 16.7232, 3.3446, 0, 17.592, 3.3446, 0, 17.592, 0, 0, 16.7232, 2.4738998, 0, 16.7232, 2.4261, 0, 20.0679, 3.3446, 0, 17.592, 3.3446, 0, 19.1425, 4.275, 0, 20.0679, 2.4261, 0, 20.0679, 3.3446, 0, 19.1425, 4.275, 0, 20.0679, 3.3446, 0, 20.986399, 2.4261, 0, 20.0679, -26.7572, -1.6710999, -17.1511, -23.412498, -2.7663999, -20.0679, -23.412498, -0.7606, -16.7232, -26.7572, -1.6710999, -17.1511, -26.7572, -1.6710999, -20.4958, -23.412498, -2.7663999, -20.0679, -29.294899, -0.6617, -13.378599, -23.412498, -0.7606, -16.7232, -23.412498, 0, -13.378599, -29.294899, -0.6617, -13.378599, -26.7572, -1.6710999, -17.1511, -23.412498, -0.7606, -16.7232, -23.412498, 0, -13.378599, -23.412498, -0.7606, -16.7232, -20.0679, -0.43429998, -16.7232, -26.7572, -1.6710999, -20.4958, -23.412498, -2.7663999, -23.412498, -23.412498, -2.7663999, -20.0679, -26.7572, -1.6710999, -20.4958, -26.7572, -2.7663999, -23.412498, -23.412498, -2.7663999, -23.412498, -23.412498, -2.7663999, -20.0679, -20.0679, -2.7663999, -23.412498, -20.0679, -1.7572, -20.0679, -23.412498, -2.7663999, -20.0679, -23.412498, -2.7663999, -23.412498, -20.0679, -2.7663999, -23.412498, -29.294899, -0.6617, -10.033899, -23.412498, 0, -13.378599, -23.412498, 0, -10.033899, -29.294899, -0.6617, -10.033899, -29.294899, -0.6617, -13.378599, -23.412498, 0, -13.378599, -29.294899, -0.6617, -6.6893, -29.294899, -0.6617, -10.033899, -23.412498, 0, -10.033899, -29.294899, -0.6617, -6.6893, -23.412498, 0, -10.033899, -23.412498, 0, -6.6893, 0, 0, -13.378599, 0, -0.9271, -16.7232, 3.0140998, 0, -16.7232, 0, -0.9271, -23.412498, 3.3446, -0.98829997, -26.7572, 3.3446, -0.3434, -23.412498, 0, -0.9271, -23.412498, 0, -1.0608, -26.7572, 3.3446, -0.98829997, -26.7572, 21.2349, 0, -6.6893, 23.412498, 0, -10.033899, 23.412498, 0, -6.6893, 21.2349, 0, -6.6893, 21.8888, 0, -10.033899, 23.412498, 0, -10.033899, 21.8888, 0, -10.033899, 21.5122, 0, -13.6136, 23.412498, 0, -10.033899, 21.8888, 0, -10.033899, 21.251299, 0, -13.3043995, 21.5122, 0, -13.6136, 21.5122, 0, -13.6136, 23.412498, 0, -13.378599, 23.412498, 0, -10.033899, 20.809599, 0, -13.7309, 21.5122, 0, -13.6136, 21.251299, 0, -13.3043995, 21.0698, 0, -13.966099, 23.412498, 0, -13.378599, 21.5122, 0, -13.6136, 20.809599, 0, -13.7309, 21.0698, 0, -13.966099, 21.5122, 0, -13.6136, 21.0698, 0, -13.966099, 20.0679, 0, -16.7232, 23.412498, 0, -13.378599, 20.809599, 0, -13.7309, 18.053999, 0, -16.7802, 21.0698, 0, -13.966099, 18.053999, 0, -16.7802, 20.0679, 0, -16.7232, 21.0698, 0, -13.966099, 20.809599, 0, -13.7309, 17.503, 0, -15.940498, 18.053999, 0, -16.7802, 17.503, 0, -15.940498, 20.809599, 0, -13.7309, 18.0241, 0, -14.301899, 16.3113, 0, -16.9629, 18.053999, 0, -16.7802, 17.503, 0, -15.940498, 17.1022, 0, -17.6197, 20.0679, 0, -16.7232, 18.053999, 0, -16.7802, 16.3113, 0, -16.9629, 17.1022, 0, -17.6197, 18.053999, 0, -16.7802, 17.1022, 0, -17.6197, 16.7232, 0, -20.0679, 20.0679, 0, -16.7232, 16.3113, 0, -16.9629, 13.748098, 0, -20.540499, 17.1022, 0, -17.6197, 13.748098, 0, -20.540499, 16.7232, 0, -20.0679, 17.1022, 0, -17.6197, 16.3113, 0, -16.9629, 13.423399, 0, -20.1574, 13.748098, 0, -20.540499, 13.423399, 0, -20.1574, 16.3113, 0, -16.9629, 13.701399, 0, -17.416399, 12.961399, 0, -20.5828, 13.748098, 0, -20.540499, 13.423399, 0, -20.1574, 13.378599, 0, -20.9734, 16.7232, 0, -20.0679, 13.748098, 0, -20.540499, 12.961399, 0, -20.5828, 13.378599, 0, -20.9734, 13.748098, 0, -20.540499, 13.378599, 0, -20.9734, 13.378599, 0, -23.412498, 16.7232, 0, -20.0679, 10.033899, 0, -20.467, 13.378599, 0, -20.9734, 12.961399, 0, -20.5828, 10.033899, 0, -23.412498, 13.378599, 0, -23.412498, 13.378599, 0, -20.9734, 10.033899, 0, -20.467, 10.033899, 0, -23.412498, 13.378599, 0, -20.9734, 6.6893, 0, -20.1318, 10.033899, 0, -23.412498, 10.033899, 0, -20.467, 6.6893, 0, -20.1318, 6.6893, 0, -23.412498, 10.033899, 0, -23.412498, 6.6893, 0, -23.412498, 6.6893, 0, -20.1318, 4.4523, 0, -20.444399, 6.6893, 0, -23.412498, 4.4523, 0, -20.444399, 3.3446, 0, -21.6384, 3.2372, 0, -19.3395, 3.3446, 0, -21.6384, 4.4523, 0, -20.444399, 3.2372, 0, -19.3395, 2.3184, 0, -20.4362, 3.3446, 0, -21.6384, 6.6893, 0, -23.412498, 10.033899, -0.8648, -26.7572, 10.033899, 0, -23.412498, 6.6893, 0, -23.412498, 6.6893, -1.0129, -26.7572, 10.033899, -0.8648, -26.7572, -20.0679, -2.7663999, -23.412498, -16.7232, -3.0929, -26.7572, -16.7232, -1.7572, -23.412498, -20.0679, -2.7663999, -23.412498, -20.0679, -3.0929, -26.7572, -16.7232, -3.0929, -26.7572, -20.0679, -1.7572, -20.0679, -16.7232, -1.7572, -23.412498, -17.2383, -0.66719997, -19.6364, -20.0679, -1.7572, -20.0679, -20.0679, -2.7663999, -23.412498, -16.7232, -1.7572, -23.412498, -17.2383, -0.66719997, -19.6364, -13.378601, -1.3177999, -23.412498, -13.210501, -0.3964, -20.032099, -17.2383, -0.66719997, -19.6364, -16.7232, -1.7572, -23.412498, -13.378601, -1.3177999, -23.412498, -20.0679, -0.36479998, 20.0679, -20.0679, -0.36479998, 16.7232, -16.7232, -0.36479998, 20.0679, -23.412498, -0.36479998, 16.7232, -20.0679, -0.36479998, 16.7232, -20.0679, -0.36479998, 20.0679, -20.0679, -0.36479998, 20.0679, -16.7232, -0.36479998, 20.0679, -16.7232, -0.36479998, 23.412498, -16.7232, -0.36479998, 20.0679, -16.7232, -0.26209998, 16.7232, -13.378601, 0, 20.0679, 20.0679, -1.8294, 20.0679, 23.412498, -1.8294, 16.7232, 23.412498, -1.8294, 20.0679, 23.412498, -1.8294, 16.7232, 26.7582, -1.8294, 20.0679, 23.412498, -1.8294, 20.0679, 23.412498, -1.8294, 16.7232, 26.7582, -1.8294, 16.7232, 26.7582, -1.8294, 20.0679, 20.0679, -1.8294, 20.0679, 20.0679, -1.8294, 16.7232, 23.412498, -1.8294, 16.7232, 20.0679, -1.8294, 16.7232, 20.0679, -1.8294, 20.0679, 16.7232, -1.8294, 20.0679, 23.412498, -1.8294, 13.378601, 23.412498, -1.8294, 16.7232, 20.0679, -1.8294, 16.7232, 23.412498, -1.8294, 13.378601, 26.7582, -1.8294, 16.7232, 23.412498, -1.8294, 16.7232, 23.412498, -1.8294, 13.378601, 26.7582, -1.8294, 13.378601, 26.7582, -1.8294, 16.7232, -6.6893, -1.3241999, -23.412498, -3.3446002, -1.458, -26.7572, -3.3446002, -1.4008, -23.412498, -6.6893, -1.3241999, -23.412498, -6.6893, -1.458, -26.7572, -3.3446002, -1.458, -26.7572, -6.6893, -0.9271, -20.0679, -3.3446002, -1.4008, -23.412498, -3.3446002, -0.9271, -20.0679, -6.6893, -0.9271, -20.0679, -6.6893, -1.3241999, -23.412498, -3.3446002, -1.4008, -23.412498, -3.3446002, -0.9271, -20.0679, 0, -0.9271, -23.412498, 0, -0.9271, -20.0679, -3.3446002, -0.9271, -20.0679, -3.3446002, -1.4008, -23.412498, 0, -0.9271, -23.412498, 20.0679, -0.8222, -23.412498, 23.412498, -1.1158999, -26.7572, 23.412498, -1.0924, -23.412498, 20.0679, -0.8222, -23.412498, 20.0679, -1.1922, -26.7572, 23.412498, -1.1158999, -26.7572, 20.0679, -0.2837, -20.0679, 20.0679, -0.8222, -23.412498, 23.412498, -1.0924, -20.0679, 26.7573, -1.3222, -26.7572, 29.564798, -1.6410999, -23.412498, 26.7582, -1.0924, -23.412498, 26.7573, -1.3222, -26.7572, 29.3928, -1.796, -26.7572, 29.564798, -1.6410999, -23.412498, 13.378599, -1.0746, 20.0679, 16.7232, -1.8294, 20.0679, 13.378599, -2.2719998, 23.3902, 13.378599, -1.0746, 20.0679, 15.0508995, -1.0746, 20.0679, 16.7232, -1.8294, 20.0679, 0, -0.9271, -20.0679, 3.3446, 0, -21.6384, 2.3184, 0, -20.4362, 0, -0.9271, -20.0679, 3.3446, -0.3434, -23.412498, 3.3446, 0, -21.6384, 0, -0.9271, -16.7232, 3.2372, 0, -19.3395, 3.0140998, 0, -16.7232, 0, -0.9271, -16.7232, 2.3184, 0, -20.4362, 3.2372, 0, -19.3395, 3.3446, 0, -21.6384, 3.3446, -0.3434, -23.412498, 6.6893, 0, -23.412498, -16.7232, 0, -16.113699, -16.7232, 0, -17.2664, -16.1493, 0, -16.8284, -16.7232, 0, -16.113699, -17.2664, 0, -16.7232, -16.7232, 0, -17.2664, -20.0679, -0.43429998, -16.7232, -16.7232, 0, -17.2664, -17.2664, 0, -16.7232, -20.0679, -0.43429998, -16.7232, -17.2383, -0.66719997, -19.6364, -16.7232, 0, -17.2664, -20.0679, -0.43429998, -16.7232, -16.7232, 0, -13.378599, -20.0679, 0, -13.378599, -16.7232, 0, -13.378599, -20.0679, -0.43429998, -16.7232, -17.2664, 0, -16.7232, -16.7232, 0, -13.378599, -17.2664, 0, -16.7232, -16.7232, 0, -16.113699, -17.2383, -0.66719997, -19.6364, -16.1493, 0, -16.8284, -16.7232, 0, -17.2664, -16.1493, 0, -16.8284, -17.2383, -0.66719997, -19.6364, -13.210501, -0.3964, -20.032099, -16.1493, 0, -16.8284, -13.210501, -0.3964, -20.032099, -13.378601, 0, -17.3364, -10.3452, 0, -16.7803, -9.7488, 0, -16.7232, -9.983101, 0, -16.4067, -10.3452, 0, -16.7803, -10.033899, 0, -17.008299, -9.7488, 0, -16.7232, -6.6893, 0, -14.263699, -9.983101, 0, -16.4067, -9.7488, 0, -16.7232, -9.983101, 0, -16.4067, -6.6893, 0, -14.263699, -9.6168995, 0, -14.127899, -6.6893, 0, -14.263699, -9.7488, 0, -16.7232, -6.6893, 0, -16.7232, -6.6893, 0, -14.263699, -6.6893, 0, -16.7232, -3.3446002, 0, -13.7127, -6.6893, 0, -14.263699, -3.3446002, 0, -13.7127, -3.6946998, 0, -13.471199, -3.6946998, 0, -13.471199, -3.3446002, 0, -13.7127, -3.0105996, 0, -13.378599, 0, 0, -13.378599, -3.0105996, 0, -13.378599, -3.3446002, 0, -13.7127, -3.6946998, 0, -13.471199, -3.0105996, 0, -13.378599, -3.2535, 0, -13.020899, 0, 0, -13.378599, -3.2535, 0, -13.020899, -3.0105996, 0, -13.378599, -2.2352996, 0, -10.6031, -3.2535, 0, -13.020899, 0, 0, -13.378599, -3.2535, 0, -13.020899, -2.2352996, 0, -10.6031, -2.7220998, 0, -10.936998, -2.2352996, 0, -10.6031, 0, 0, -13.378599, 0, 0, -10.7654, -13.210501, -0.3964, -20.032099, -10.3452, 0, -16.7803, -13.378601, 0, -17.3364, -10.3452, 0, -16.7803, -13.210501, -0.3964, -20.032099, -9.318001, -0.3969, -19.413, -10.3452, 0, -16.7803, -9.318001, -0.3969, -19.413, -10.033899, 0, -17.008299, -10.033899, 0, -17.008299, -6.6893, 0, -16.7232, -9.7488, 0, -16.7232, -10.033899, 0, -17.008299, -9.318001, -0.3969, -19.413, -6.6893, 0, -16.7232, -3.3446002, 0, -13.7127, 0, -0.9271, -16.7232, 0, 0, -13.378599, -3.3446002, 0, -13.7127, -3.3446002, -0.9271, -16.7232, 0, -0.9271, -16.7232, 0, 0, -13.378599, 2.2182999, 0, -10.525, 0, 0, -10.7654, 2.2182999, 0, -10.525, 0, 0, -13.378599, 3.3446, -0.3989, -12.9443, 2.2182999, 0, -10.525, 3.3446, -0.3989, -12.9443, 2.9573, -0.1021, -11.0952, -20.108599, 0, -9.592199, -20.0679, 0, -10.476398, -19.6286, 0, -10.1098995, -20.108599, 0, -9.592199, -20.5103, 0, -10.033899, -20.0679, 0, -10.476398, -20.3764, 0, -6.6893, -20.5103, 0, -10.033899, -20.108599, 0, -9.592199, -20.0679, 0, -13.378599, -19.6286, 0, -10.1098995, -20.0679, 0, -10.476398, -20.5103, 0, -10.033899, -20.0679, 0, -13.378599, -20.0679, 0, -10.476398, -19.6286, 0, -10.1098995, -20.0679, 0, -13.378599, -17.1586, 0, -11.120298, -19.6286, 0, -10.1098995, -17.1586, 0, -11.120298, -17.8929, 0, -10.41, -17.1586, 0, -11.120298, -20.0679, 0, -13.378599, -16.7232, 0, -13.378599, -20.5103, 0, -10.033899, -23.412498, 0, -13.378599, -20.0679, 0, -13.378599, -23.412498, 0, -13.378599, -20.5103, 0, -10.033899, -23.412498, 0, -10.033899, -20.3764, 0, -6.6893, -23.412498, 0, -10.033899, -20.5103, 0, -10.033899, -23.412498, 0, -10.033899, -20.3764, 0, -6.6893, -23.412498, 0, -6.6893, -23.412498, -0.0385, -2.8684, -23.412498, 0, -3.7743, -22.9552, -0.047599997, -3.375, -23.412498, -0.0385, -2.8684, -23.8421, 0, -3.3446002, -23.412498, 0, -3.7743, -23.412498, 0, -6.6893, -22.9552, -0.047599997, -3.375, -23.412498, 0, -3.7743, -22.9552, -0.047599997, -3.375, -23.412498, 0, -6.6893, -20.1607, -0.2436, -4.5060997, -20.1607, -0.2436, -4.5060997, -23.412498, 0, -6.6893, -20.3764, 0, -6.6893, -22.9552, -0.047599997, -3.375, -20.1607, -0.2436, -4.5060997, -21.0086, -0.2504, -3.5043998, -29.294899, -0.6617, -6.6893, -23.8421, 0, -3.3446002, -27.9812, -0.6617, -3.3446002, -23.8421, 0, -3.3446002, -29.294899, -0.6617, -6.6893, -23.412498, 0, -6.6893, -23.8421, 0, -3.3446002, -23.412498, 0, -6.6893, -23.412498, 0, -3.7743, -27.9812, -0.6617, -3.3446002, -25.479, -0.4588, 0.143, -27.9812, -0.6617, 0, -25.479, -0.4588, 0.143, -27.9812, -0.6617, -3.3446002, -23.412498, -0.0385, -2.8684, -23.412498, -0.0385, -2.8684, -27.9812, -0.6617, -3.3446002, -23.8421, 0, -3.3446002, -25.479, -0.4588, 0.143, -23.412498, -0.0385, -2.8684, -23.412498, -0.2913, 0.26119995, -22.756899, 0, 10.033899, -24.056599, 0, 10.033899, -23.489, 0, 9.386499, -22.756899, 0, 10.033899, -23.412498, 0, 10.6779995, -24.056599, 0, 10.033899, -20.0679, 0, 13.378601, -23.412498, 0, 10.6779995, -22.756899, 0, 10.033899, -23.412498, 0, 10.6779995, -20.0679, 0, 13.378601, -23.412498, 0, 13.378601, -20.0679, 0, 13.378601, -22.756899, 0, 10.033899, -20.0679, 0, 10.033899, -20.0679, 0, 10.033899, -17.213, 0, 13.378601, -20.0679, 0, 13.378601, -17.213, 0, 13.378601, -20.0679, 0, 10.033899, -16.7232, 0, 10.8878, -16.7232, 0, 10.8878, -20.0679, 0, 10.033899, -17.583, 0, 10.033899, -17.213, 0, 13.378601, -16.7232, 0, 10.8878, -16.7232, 0, 12.878401, -16.223, 0, 13.378601, -17.213, 0, 13.378601, -16.7232, 0, 12.878401, -16.223, 0, 13.378601, -16.7232, 0, 13.868401, -17.213, 0, 13.378601, -29.294899, -0.6617, 10.033899, -25.945799, -0.25779998, 6.6893, -26.0077, -0.2465, 10.033899, -29.294899, -0.6617, 10.033899, -29.294899, -0.6617, 6.6893, -25.945799, -0.25779998, 6.6893, -29.294899, -0.6617, 13.378601, -26.0077, -0.2465, 10.033899, -25.9681, -0.2875, 13.378601, -29.294899, -0.6617, 13.378601, -29.294899, -0.6617, 10.033899, -26.0077, -0.2465, 10.033899, -20.0679, -0.36479998, 16.7232, -20.0679, 0, 13.378601, -16.7232, -0.26209998, 16.7232, -16.7232, 0, 13.868401, -13.378601, 0, 16.7232, -16.7232, -0.26209998, 16.7232, -13.378601, 0, 16.7232, -16.7232, 0, 13.868401, -16.223, 0, 13.378601, -13.378601, 0, 16.7232, -16.223, 0, 13.378601, -13.378601, 0, 13.378601, 0, 0, 20.0679, 3.3446, -0.2814, 23.412498, 0, -0.2814, 23.412498, 3.3446, -0.2814, 23.412498, 0, 0, 20.0679, 2.4261, 0, 20.0679, 3.3446, -0.2814, 23.412498, 2.4261, 0, 20.0679, 3.3446, 0, 20.986399, 3.3446, 0, 20.986399, 6.6893, -0.2814, 23.412498, 3.3446, -0.2814, 23.412498, 6.6893, -0.2814, 23.412498, 3.3446, 0, 20.986399, 4.275, 0, 20.0679, 6.6893, -0.2814, 23.412498, 4.275, 0, 20.0679, 6.6893, 0, 20.0679, 10.572599, 0, 19.5895, 10.501598, 0, 20.5087, 10.080299, 0, 20.0679, 10.572599, 0, 19.5895, 11.0191, 0, 20.0679, 10.501598, 0, 20.5087, 10.572599, 0, 19.5895, 13.378599, 0, 16.7232, 11.0191, 0, 20.0679, 10.572599, 0, 19.5895, 10.572599, 0, 16.7232, 13.378599, 0, 16.7232, 10.572599, 0, 14.3402, 13.378599, 0, 16.7232, 10.572599, 0, 16.7232, 13.378599, 0, 16.7232, 10.572599, 0, 14.3402, 13.105099, 0, 13.9188, 13.105099, 0, 13.9188, 10.572599, 0, 14.3402, 10.986898, 0, 13.9188, 13.378599, 0, 16.7232, 13.105099, 0, 13.9188, 13.700699, 0, 14.602699, 13.8046, 0, 13.2202, 13.700699, 0, 14.602699, 13.105099, 0, 13.9188, 13.8046, 0, 13.2202, 14.439199, 0, 13.6437, 13.700699, 0, 14.602699, 6.6893, -0.2814, 23.412498, 10.501598, 0, 20.5087, 10.033899, -0.501, 23.412498, 10.033899, -0.501, 23.412498, 11.0191, 0, 20.0679, 13.378599, -1.0746, 20.0679, 10.033899, -0.501, 23.412498, 10.501598, 0, 20.5087, 11.0191, 0, 20.0679, 17.4912, 0.0034999999, 7.4080997, 13.8046, 0, 10.033899, 14.0257, 0.0442, 7.3252997, 13.8046, 0, 10.033899, 17.4912, 0.0034999999, 7.4080997, 16.7232, 0, 10.033899, 13.8046, 0, 10.033899, 16.7232, 0, 10.033899, 15.0508995, 0, 10.033899, 16.7232, -1.0746, 13.378601, 15.0508995, -0.40039998, 16.7232, 15.0508995, -0.6288, 13.378601, -16.7232, -1.1374999, 26.7572, -20.0679, -1.9081, 30.3625, -20.0679, -1.1374999, 26.7572, -16.7232, -1.1374999, 26.7572, -16.7232, -1.9068999, 30.3568, -20.0679, -1.9081, 30.3625, -23.412498, -1.9093, 30.3681, -20.0679, -1.1374999, 26.7572, -20.0679, -1.9081, 30.3625, -23.412498, -1.9093, 30.3681, -23.412498, -1.1374999, 26.7572, -20.0679, -1.1374999, 26.7572, -23.412498, -2.6287, 33.7341, -23.412498, -1.9093, 30.3681, -20.0679, -1.9081, 30.3625, -16.7232, -1.9068999, 30.3568, -20.0679, -2.628, 33.7306, -20.0679, -1.9081, 30.3625, -23.412498, -2.6287, 33.7341, -20.0679, -1.9081, 30.3625, -20.0679, -2.628, 33.7306, -16.7232, -1.9068999, 30.3568, -16.7232, -2.6272, 33.726997, -20.0679, -2.628, 33.7306, -23.412498, -3.2477999, 36.6305, -23.412498, -2.6287, 33.7341, -20.0679, -2.628, 33.7306, -16.7232, -2.6272, 33.726997, -20.0679, -3.2473998, 36.6287, -20.0679, -2.628, 33.7306, -23.412498, -3.2477999, 36.6305, -20.0679, -2.628, 33.7306, -20.0679, -3.2473998, 36.6287, -16.7232, -2.6272, 33.726997, -16.7232, -3.247, 36.6269, -20.0679, -3.2473998, 36.6287, -20.0679, -3.8518999, 39.456898, -23.412498, -3.2477999, 36.6305, -20.0679, -3.2473998, 36.6287, -16.7232, -3.247, 36.6269, -20.0679, -3.8518999, 39.456898, -20.0679, -3.2473998, 36.6287, -20.0679, -3.8518999, 39.456898, -23.412498, -3.8518999, 39.456898, -23.412498, -3.2477999, 36.6305, -16.7232, -3.247, 36.6269, -16.7232, -3.8518999, 39.456898, -20.0679, -3.8518999, 39.456898, -27.9812, -0.6617, 0, -30.315699, -0.9931, 3.3446, -30.300001, -1.3205999, 0, -27.9812, -0.6617, 0, -29.294899, -0.6617, 3.3446, -30.315699, -0.9931, 3.3446, -50.290497, -8.1437, 10.033899, -53.117798, -8.6939, 6.6893, -50.290497, -8.1437, 6.6893, -50.290497, -8.1437, 10.033899, -50.290497, -8.1437, 13.378601, -53.117798, -8.6939, 6.6893, -53.117798, -8.6939, 3.3446, -50.290497, -8.1437, 6.6893, -53.117798, -8.6939, 6.6893, -53.117798, -8.6939, 3.3446, -50.290497, -8.1437, 3.3446, -50.290497, -8.1437, 6.6893, -53.117798, -8.6939, 6.6893, -50.290497, -8.1437, 13.378601, -53.117798, -8.6939, 13.378601, -53.117798, -8.6939, 0, -50.290497, -8.1437, 3.3446, -53.117798, -8.6939, 3.3446, -53.117798, -8.6939, 0, -50.290497, -8.1437, 0, -50.290497, -8.1437, 3.3446, -55.5951, -9.176, 3.3446, -53.117798, -8.6939, 3.3446, -53.117798, -8.6939, 6.6893, -53.117798, -8.6939, -3.3446002, -50.290497, -8.1437, 0, -53.117798, -8.6939, 0, -53.117798, -8.6939, -3.3446002, -50.290497, -8.1437, -3.3446002, -50.290497, -8.1437, 0, -55.5951, -9.176, 0, -53.117798, -8.6939, 0, -53.117798, -8.6939, 3.3446, -55.5951, -9.176, 0, -53.117798, -8.6939, 3.3446, -55.5951, -9.176, 3.3446, -55.5951, -9.176, -3.3446002, -53.117798, -8.6939, -3.3446002, -53.117798, -8.6939, 0, -55.5951, -9.176, -3.3446002, -53.117798, -8.6939, 0, -55.5951, -9.176, 0, -53.117798, -8.6939, -6.6893, -50.290497, -8.1437, -3.3446002, -53.117798, -8.6939, -3.3446002, -53.117798, -8.6939, -6.6893, -50.290497, -8.1437, -6.6893, -50.290497, -8.1437, -3.3446002, -55.5951, -9.176, -6.6893, -53.117798, -8.6939, -3.3446002, -55.5951, -9.176, -3.3446002, -55.5951, -9.176, -6.6893, -53.117798, -8.6939, -6.6893, -53.117798, -8.6939, -3.3446002, -53.117798, -8.6939, -10.033899, -50.290497, -8.1437, -6.6893, -53.117798, -8.6939, -6.6893, -53.117798, -8.6939, -10.033899, -50.290497, -8.1437, -10.033899, -50.290497, -8.1437, -6.6893, -55.5951, -9.176, -10.033899, -53.117798, -8.6939, -6.6893, -55.5951, -9.176, -6.6893, -55.5951, -9.176, -10.033899, -53.117798, -8.6939, -10.033899, -53.117798, -8.6939, -6.6893, -53.117798, -8.6939, -13.378599, -50.290497, -8.1437, -10.033899, -53.117798, -8.6939, -10.033899, -53.117798, -8.6939, -13.378599, -50.290497, -8.1437, -13.378599, -50.290497, -8.1437, -10.033899, -53.117798, -8.6939, -13.378599, -50.290497, -8.1437, -16.7232, -50.290497, -8.1437, -13.378599, -53.117798, -8.6939, -13.378599, -50.290497, -8.1437, -16.937199, -50.290497, -8.1437, -16.7232, -50.290497, -8.1437, -16.937199, -53.117798, -8.6939, -13.378599, -53.117798, -8.6939, -16.911499, -53.117798, -8.6939, -16.911499, -50.290497, -8.1437, -20.0679, -50.290497, -8.1437, -16.937199, -55.5951, -9.176, -13.378599, -53.117798, -8.6939, -13.378599, -53.117798, -8.6939, -10.033899, -55.5951, -9.176, -13.378599, -53.117798, -8.6939, -10.033899, -55.5951, -9.176, -10.033899, -53.117798, -8.6939, -16.911499, -50.290497, -8.1437, -20.2818, -50.290497, -8.1437, -20.0679, -55.5951, -9.176, -16.888899, -53.117798, -8.6939, -13.378599, -55.5951, -9.176, -13.378599, -55.5951, -9.176, -16.888899, -53.117798, -8.6939, -16.911499, -53.117798, -8.6939, -13.378599, -58.463497, -9.7343, -13.378599, -55.5951, -9.176, -13.378599, -55.5951, -9.176, -10.033899, -58.463497, -9.7343, -16.8629, -55.5951, -9.176, -16.888899, -55.5951, -9.176, -13.378599, -58.463497, -9.7343, -16.8629, -55.5951, -9.176, -13.378599, -58.463497, -9.7343, -13.378599, -55.5951, -9.176, -20.233599, -53.117798, -8.6939, -16.911499, -55.5951, -9.176, -16.888899, -50.290497, -8.1437, -20.2818, -53.117798, -8.6939, -16.911499, -53.117798, -8.6939, -20.2561, -55.5951, -9.176, -20.233599, -53.117798, -8.6939, -20.2561, -53.117798, -8.6939, -16.911499, -53.117798, -8.6939, -23.412498, -50.290497, -8.1437, -20.2818, -53.117798, -8.6939, -20.2561, -53.117798, -8.6939, -23.412498, -50.290497, -8.1437, -23.412498, -50.290497, -8.1437, -20.2818, -53.117798, -8.6939, -26.7572, -50.290497, -8.1437, -23.412498, -53.117798, -8.6939, -23.412498, -53.117798, -8.6939, -26.7572, -50.290497, -8.1437, -26.7572, -50.290497, -8.1437, -23.412498, -55.5951, -9.176, -26.7572, -53.117798, -8.6939, -26.7572, -53.117798, -8.6939, -23.412498, -55.5951, -9.176, -23.412498, -53.117798, -8.6939, -23.412498, -53.117798, -8.6939, -20.2561, -55.5951, -9.176, -26.7572, -53.117798, -8.6939, -23.412498, -55.5951, -9.176, -23.412498, -55.5951, -9.176, -23.412498, -53.117798, -8.6939, -20.2561, -55.5951, -9.176, -20.233599, -58.463497, -9.7343, -26.7572, -55.5951, -9.176, -26.7572, -55.5951, -9.176, -23.412498, -58.463497, -9.7343, -23.412498, -55.5951, -9.176, -23.412498, -55.5951, -9.176, -20.233599, -58.463497, -9.7343, -26.7572, -55.5951, -9.176, -23.412498, -58.463497, -9.7343, -23.412498, -62.057194, -10.4337, -23.412498, -58.463497, -9.7343, -26.7572, -58.463497, -9.7343, -23.412498, -62.057194, -10.4337, -23.412498, -62.057194, -10.4337, -26.7572, -58.463497, -9.7343, -26.7572, -62.057194, -10.4337, -20.174799, -62.057194, -10.4337, -23.412498, -58.463497, -9.7343, -23.412498, -58.463497, -9.7343, -23.412498, -55.5951, -9.176, -20.233599, -58.463497, -9.7343, -20.207499, -62.057194, -10.4337, -20.174799, -58.463497, -9.7343, -23.412498, -58.463497, -9.7343, -20.207499, -58.463497, -9.7343, -20.207499, -55.5951, -9.176, -20.233599, -55.5951, -9.176, -16.888899, -58.463497, -9.7343, -20.207499, -55.5951, -9.176, -16.888899, -58.463497, -9.7343, -16.8629, -58.463497, -9.7343, -16.8629, -62.057194, -10.4337, -20.174799, -58.463497, -9.7343, -20.207499, -58.463497, -9.7343, -16.8629, -62.057194, -10.4337, -20.0679, -62.057194, -10.4337, -20.174799, -62.057194, -10.4337, -20.0679, -58.463497, -9.7343, -16.8629, -62.057194, -10.4337, -16.8302, -58.463497, -9.7343, -13.378599, -62.057194, -10.4337, -16.8302, -58.463497, -9.7343, -16.8629, -58.463497, -9.7343, -13.378599, -62.057194, -10.4337, -16.7232, -62.057194, -10.4337, -16.8302, -62.057194, -10.4337, -16.7232, -58.463497, -9.7343, -13.378599, -62.057194, -10.4337, -13.378599, -62.057194, -10.4337, -10.033899, -62.057194, -10.4337, -13.378599, -58.463497, -9.7343, -13.378599, -62.057194, -10.4337, -10.033899, -58.463497, -9.7343, -13.378599, -58.463497, -9.7343, -10.033899, -58.463497, -9.7343, -13.378599, -55.5951, -9.176, -10.033899, -58.463497, -9.7343, -10.033899, -62.057194, -10.4337, -6.6893, -62.057194, -10.4337, -10.033899, -58.463497, -9.7343, -10.033899, -58.463497, -9.7343, -10.033899, -55.5951, -9.176, -10.033899, -55.5951, -9.176, -6.6893, -62.057194, -10.4337, -6.6893, -58.463497, -9.7343, -10.033899, -58.463497, -9.7343, -6.6893, -58.463497, -9.7343, -10.033899, -55.5951, -9.176, -6.6893, -58.463497, -9.7343, -6.6893, -62.057194, -10.4337, -3.3446002, -62.057194, -10.4337, -6.6893, -58.463497, -9.7343, -6.6893, -58.463497, -9.7343, -6.6893, -55.5951, -9.176, -6.6893, -55.5951, -9.176, -3.3446002, -62.057194, -10.4337, -3.3446002, -58.463497, -9.7343, -6.6893, -58.463497, -9.7343, -3.3446002, -58.463497, -9.7343, -6.6893, -55.5951, -9.176, -3.3446002, -58.463497, -9.7343, -3.3446002, -62.057194, -10.4337, 0, -62.057194, -10.4337, -3.3446002, -58.463497, -9.7343, -3.3446002, -58.463497, -9.7343, -3.3446002, -55.5951, -9.176, -3.3446002, -55.5951, -9.176, 0, -62.057194, -10.4337, 0, -58.463497, -9.7343, -3.3446002, -58.463497, -9.7343, 0, -58.463497, -9.7343, -3.3446002, -55.5951, -9.176, 0, -58.463497, -9.7343, 0, -62.057194, -10.4337, 3.3446, -62.057194, -10.4337, 0, -58.463497, -9.7343, 0, -58.463497, -9.7343, 0, -55.5951, -9.176, 0, -55.5951, -9.176, 3.3446, -62.057194, -10.4337, 3.3446, -58.463497, -9.7343, 0, -58.463497, -9.7343, 3.3446, -58.463497, -9.7343, 0, -55.5951, -9.176, 3.3446, -58.463497, -9.7343, 3.3446, -62.057194, -10.4337, 6.6893, -62.057194, -10.4337, 3.3446, -58.463497, -9.7343, 3.3446, -62.057194, -10.4337, 6.6893, -58.463497, -9.7343, 3.3446, -58.463497, -9.7343, 6.6893, -58.463497, -9.7343, 6.6893, -62.057194, -10.4337, 10.033899, -62.057194, -10.4337, 6.6893, -58.463497, -9.7343, 3.3446, -55.5951, -9.176, 3.3446, -55.5951, -9.176, 6.6893, -58.463497, -9.7343, 3.3446, -55.5951, -9.176, 6.6893, -58.463497, -9.7343, 6.6893, -55.5951, -9.176, 3.3446, -53.117798, -8.6939, 6.6893, -55.5951, -9.176, 6.6893, -58.463497, -9.7343, 6.6893, -62.057194, -10.4337, 13.378601, -62.057194, -10.4337, 10.033899, -53.117798, -8.6939, 13.378601, -55.5951, -9.176, 6.6893, -53.117798, -8.6939, 6.6893, -55.5951, -9.176, 13.378601, -58.463497, -9.7343, 6.6893, -55.5951, -9.176, 6.6893, -53.117798, -8.6939, 13.378601, -55.5951, -9.176, 13.378601, -55.5951, -9.176, 6.6893, -55.5951, -9.176, 13.378601, -58.463497, -9.7343, 13.378601, -58.463497, -9.7343, 6.6893, -62.057194, -10.4337, 13.378601, -58.463497, -9.7343, 6.6893, -58.463497, -9.7343, 13.378601, -53.117798, -8.6939, 16.7232, -55.5951, -9.176, 13.378601, -53.117798, -8.6939, 13.378601, -50.290497, -8.1437, 13.378601, -53.117798, -8.6939, 16.7232, -53.117798, -8.6939, 13.378601, -50.290497, -8.1437, 13.378601, -50.290497, -8.1437, 16.7232, -53.117798, -8.6939, 16.7232, -53.117798, -8.6939, 16.7232, -55.5951, -9.176, 16.7232, -55.5951, -9.176, 13.378601, -55.5951, -9.176, 16.7232, -58.463497, -9.7343, 13.378601, -55.5951, -9.176, 13.378601, -50.290497, -8.1437, 16.7232, -53.117798, -8.6939, 20.0679, -53.117798, -8.6939, 16.7232, -53.117798, -8.6939, 20.0679, -55.5951, -9.176, 16.7232, -53.117798, -8.6939, 16.7232, -50.290497, -8.1437, 16.7232, -50.290497, -8.1437, 20.0679, -53.117798, -8.6939, 20.0679, -58.463497, -9.7343, 16.7232, -62.057194, -10.4337, 13.378601, -58.463497, -9.7343, 13.378601, -55.5951, -9.176, 16.7232, -58.463497, -9.7343, 16.7232, -58.463497, -9.7343, 13.378601, -58.463497, -9.7343, 16.7232, -62.057194, -10.4337, 16.7232, -62.057194, -10.4337, 13.378601, -53.117798, -8.6939, 20.0679, -55.5951, -9.176, 20.0679, -55.5951, -9.176, 16.7232, -55.5951, -9.176, 20.0679, -58.463497, -9.7343, 16.7232, -55.5951, -9.176, 16.7232, -50.290497, -8.1437, 20.0679, -53.117798, -8.6939, 23.412498, -53.117798, -8.6939, 20.0679, -53.117798, -8.6939, 23.412498, -55.5951, -9.176, 20.0679, -53.117798, -8.6939, 20.0679, -50.290497, -8.1437, 20.0679, -50.290497, -8.1437, 23.412498, -53.117798, -8.6939, 23.412498, -50.290497, -8.1437, 23.412498, -53.117798, -8.6939, 26.7572, -53.117798, -8.6939, 23.412498, -50.290497, -8.1437, 23.412498, -50.290497, -8.1437, 26.7572, -53.117798, -8.6939, 26.7572, -53.117798, -8.6939, 26.7572, -55.5951, -9.176, 23.412498, -53.117798, -8.6939, 23.412498, -53.117798, -8.6939, 23.412498, -55.5951, -9.176, 23.412498, -55.5951, -9.176, 20.0679, -53.117798, -8.6939, 26.7572, -55.5951, -9.176, 26.7572, -55.5951, -9.176, 23.412498, -55.5951, -9.176, 20.0679, -58.463497, -9.7343, 20.0679, -58.463497, -9.7343, 16.7232, -58.463497, -9.7343, 20.0679, -62.057194, -10.4337, 16.7232, -58.463497, -9.7343, 16.7232, -55.5951, -9.176, 23.412498, -58.463497, -9.7343, 20.0679, -55.5951, -9.176, 20.0679, -58.463497, -9.7343, 20.0679, -62.057194, -10.4337, 20.0679, -62.057194, -10.4337, 16.7232, -55.5951, -9.176, 26.7572, -58.463497, -9.7343, 23.412498, -55.5951, -9.176, 23.412498, -55.5951, -9.176, 23.412498, -58.463497, -9.7343, 23.412498, -58.463497, -9.7343, 20.0679, -58.463497, -9.7343, 23.412498, -62.057194, -10.4337, 20.0679, -58.463497, -9.7343, 20.0679, -55.5951, -9.176, 26.7572, -58.463497, -9.7343, 26.7572, -58.463497, -9.7343, 23.412498, -58.463497, -9.7343, 23.412498, -62.057194, -10.4337, 23.412498, -62.057194, -10.4337, 20.0679, -58.463497, -9.7343, 26.7572, -62.057194, -10.4337, 23.412498, -58.463497, -9.7343, 23.412498, -58.463497, -9.7343, 26.7572, -62.057194, -10.4337, 26.7572, -62.057194, -10.4337, 23.412498, -62.057194, -10.4337, 0, -64.963196, -10.4337, -3.3446002, -62.057194, -10.4337, -3.3446002, -64.963196, -10.4337, -6.6893, -62.057194, -10.4337, -3.3446002, -64.963196, -10.4337, -3.3446002, -64.963196, -10.4337, -6.6893, -62.057194, -10.4337, -6.6893, -62.057194, -10.4337, -3.3446002, -62.057194, -10.4337, 0, -64.963196, -10.4337, 0, -64.963196, -10.4337, -3.3446002, -64.963196, -10.4337, 0, -62.057194, -10.4337, 0, -62.057194, -10.4337, 3.3446, -68.2489, -10.4337, -6.6893, -64.963196, -10.4337, -6.6893, -64.963196, -10.4337, -3.3446002, -64.963196, -10.4337, 0, -68.2489, -10.4337, -3.3446002, -64.963196, -10.4337, -3.3446002, -68.2489, -10.4337, -6.6893, -64.963196, -10.4337, -3.3446002, -68.2489, -10.4337, -3.3446002, -64.963196, -10.4337, -10.033899, -62.057194, -10.4337, -6.6893, -64.963196, -10.4337, -6.6893, -64.963196, -10.4337, -10.033899, -62.057194, -10.4337, -10.033899, -62.057194, -10.4337, -6.6893, -68.2489, -10.4337, -10.033899, -64.963196, -10.4337, -6.6893, -68.2489, -10.4337, -6.6893, -68.2489, -10.4337, -10.033899, -64.963196, -10.4337, -10.033899, -64.963196, -10.4337, -6.6893, -64.963196, -10.4337, -13.378599, -62.057194, -10.4337, -10.033899, -64.963196, -10.4337, -10.033899, -64.963196, -10.4337, -13.378599, -62.057194, -10.4337, -13.378599, -62.057194, -10.4337, -10.033899, -64.963196, -10.4337, -13.378599, -62.057194, -10.4337, -16.7232, -62.057194, -10.4337, -13.378599, -64.963196, -10.4337, -13.378599, -62.057194, -10.4337, -16.8302, -62.057194, -10.4337, -16.7232, -62.057194, -10.4337, -16.8302, -64.963196, -10.4337, -13.378599, -64.963196, -10.4337, -16.803799, -64.963196, -10.4337, -16.803799, -62.057194, -10.4337, -20.0679, -62.057194, -10.4337, -16.8302, -68.2489, -10.4337, -13.378599, -64.963196, -10.4337, -13.378599, -64.963196, -10.4337, -10.033899, -68.2489, -10.4337, -13.378599, -64.963196, -10.4337, -10.033899, -68.2489, -10.4337, -10.033899, -64.963196, -10.4337, -16.803799, -62.057194, -10.4337, -20.174799, -62.057194, -10.4337, -20.0679, -68.2489, -10.4337, -16.7739, -64.963196, -10.4337, -13.378599, -68.2489, -10.4337, -13.378599, -68.2489, -10.4337, -16.7739, -64.963196, -10.4337, -16.803799, -64.963196, -10.4337, -13.378599, -71.2999, -10.4337, -13.378599, -68.2489, -10.4337, -13.378599, -68.2489, -10.4337, -10.033899, -71.2999, -10.4337, -16.746199, -68.2489, -10.4337, -16.7739, -68.2489, -10.4337, -13.378599, -71.2999, -10.4337, -16.746199, -68.2489, -10.4337, -13.378599, -71.2999, -10.4337, -13.378599, -73.8239, -10.4337, -13.378599, -71.2999, -10.4337, -16.746199, -71.2999, -10.4337, -13.378599, -73.8239, -10.4337, -13.378599, -73.8239, -10.4337, -16.7232, -71.2999, -10.4337, -16.746199, -73.8239, -10.4337, -10.033899, -73.8239, -10.4337, -13.378599, -71.2999, -10.4337, -13.378599, -73.8239, -10.4337, -16.7232, -71.2999, -10.4337, -20.0908, -71.2999, -10.4337, -16.746199, -71.2999, -10.4337, -20.0908, -68.2489, -10.4337, -16.7739, -71.2999, -10.4337, -16.746199, -73.8239, -10.4337, -16.7232, -73.8239, -10.4337, -20.0679, -71.2999, -10.4337, -20.0908, -71.2999, -10.4337, -13.378599, -68.2489, -10.4337, -10.033899, -71.2999, -10.4337, -10.033899, -73.8239, -10.4337, -10.033899, -71.2999, -10.4337, -13.378599, -71.2999, -10.4337, -10.033899, -71.2999, -10.4337, -10.033899, -68.2489, -10.4337, -10.033899, -68.2489, -10.4337, -6.6893, -73.8239, -10.4337, -6.6893, -73.8239, -10.4337, -10.033899, -71.2999, -10.4337, -10.033899, -71.2999, -10.4337, -10.033899, -68.2489, -10.4337, -6.6893, -71.2999, -10.4337, -6.6893, -73.8239, -10.4337, -6.6893, -71.2999, -10.4337, -10.033899, -71.2999, -10.4337, -6.6893, -71.2999, -10.4337, -6.6893, -68.2489, -10.4337, -6.6893, -68.2489, -10.4337, -3.3446002, -73.8239, -10.4337, -3.3446002, -73.8239, -10.4337, -6.6893, -71.2999, -10.4337, -6.6893, -71.2999, -10.4337, -6.6893, -68.2489, -10.4337, -3.3446002, -71.2999, -10.4337, -3.3446002, -73.8239, -10.4337, -3.3446002, -71.2999, -10.4337, -6.6893, -71.2999, -10.4337, -3.3446002, -68.2489, -10.4337, 0, -71.2999, -10.4337, -3.3446002, -68.2489, -10.4337, -3.3446002, -71.2999, -10.4337, 0, -73.8239, -10.4337, -3.3446002, -71.2999, -10.4337, -3.3446002, -68.2489, -10.4337, 0, -71.2999, -10.4337, 0, -71.2999, -10.4337, -3.3446002, -71.2999, -10.4337, 0, -73.8239, -10.4337, 0, -73.8239, -10.4337, -3.3446002, -64.963196, -10.4337, 0, -68.2489, -10.4337, 0, -68.2489, -10.4337, -3.3446002, -73.8239, -10.4337, 0, -71.2999, -10.4337, 0, -71.2999, -10.4337, 3.3446, -73.8239, -10.4337, 0, -71.2999, -10.4337, 3.3446, -73.8239, -10.4337, 3.3446, -71.2999, -10.4337, 0, -68.2489, -10.4337, 0, -68.2489, -10.4337, 3.3446, -71.2999, -10.4337, 0, -68.2489, -10.4337, 3.3446, -71.2999, -10.4337, 3.3446, -68.2489, -10.4337, 0, -64.963196, -10.4337, 0, -64.963196, -10.4337, 3.3446, -68.2489, -10.4337, 0, -64.963196, -10.4337, 3.3446, -68.2489, -10.4337, 3.3446, -64.963196, -10.4337, 0, -62.057194, -10.4337, 3.3446, -64.963196, -10.4337, 3.3446, -62.057194, -10.4337, 3.3446, -64.963196, -10.4337, 6.6893, -64.963196, -10.4337, 3.3446, -64.963196, -10.4337, 6.6893, -68.2489, -10.4337, 3.3446, -64.963196, -10.4337, 3.3446, -62.057194, -10.4337, 3.3446, -62.057194, -10.4337, 6.6893, -64.963196, -10.4337, 6.6893, -62.057194, -10.4337, 10.033899, -64.963196, -10.4337, 6.6893, -62.057194, -10.4337, 6.6893, -64.963196, -10.4337, 6.6893, -62.057194, -10.4337, 10.033899, -62.057194, -10.4337, 13.378601, -64.963196, -10.4337, 6.6893, -68.2489, -10.4337, 6.6893, -68.2489, -10.4337, 3.3446, -68.2489, -10.4337, 6.6893, -71.2999, -10.4337, 3.3446, -68.2489, -10.4337, 3.3446, -64.963196, -10.4337, 6.6893, -62.057194, -10.4337, 13.378601, -64.963196, -10.4337, 13.378601, -64.963196, -10.4337, 13.378601, -68.2489, -10.4337, 6.6893, -64.963196, -10.4337, 6.6893, -62.057194, -10.4337, 13.378601, -64.963196, -10.4337, 16.7232, -64.963196, -10.4337, 13.378601, -62.057194, -10.4337, 13.378601, -62.057194, -10.4337, 16.7232, -64.963196, -10.4337, 16.7232, -64.963196, -10.4337, 13.378601, -68.2489, -10.4337, 13.378601, -68.2489, -10.4337, 6.6893, -64.963196, -10.4337, 16.7232, -68.2489, -10.4337, 13.378601, -64.963196, -10.4337, 13.378601, -68.2489, -10.4337, 6.6893, -71.2999, -10.4337, 6.6893, -71.2999, -10.4337, 3.3446, -68.2489, -10.4337, 13.378601, -71.2999, -10.4337, 6.6893, -68.2489, -10.4337, 6.6893, -71.2999, -10.4337, 6.6893, -73.8239, -10.4337, 3.3446, -71.2999, -10.4337, 3.3446, -71.2999, -10.4337, 6.6893, -73.8239, -10.4337, 6.6893, -73.8239, -10.4337, 3.3446, -71.2999, -10.4337, 6.6893, -73.8239, -10.4337, 10.033899, -73.8239, -10.4337, 6.6893, -71.2999, -10.4337, 6.6893, -73.8239, -10.4337, 13.378601, -73.8239, -10.4337, 10.033899, -68.2489, -10.4337, 13.378601, -71.2999, -10.4337, 13.378601, -71.2999, -10.4337, 6.6893, -73.8239, -10.4337, 13.378601, -71.2999, -10.4337, 6.6893, -71.2999, -10.4337, 13.378601, -64.963196, -10.4337, 16.7232, -68.2489, -10.4337, 16.7232, -68.2489, -10.4337, 13.378601, -68.2489, -10.4337, 16.7232, -71.2999, -10.4337, 13.378601, -68.2489, -10.4337, 13.378601, -71.2999, -10.4337, 16.7232, -73.8239, -10.4337, 13.378601, -71.2999, -10.4337, 13.378601, -68.2489, -10.4337, 16.7232, -71.2999, -10.4337, 16.7232, -71.2999, -10.4337, 13.378601, -71.2999, -10.4337, 16.7232, -73.8239, -10.4337, 16.7232, -73.8239, -10.4337, 13.378601, -64.963196, -10.4337, 20.0679, -68.2489, -10.4337, 16.7232, -64.963196, -10.4337, 16.7232, -62.057194, -10.4337, 16.7232, -64.963196, -10.4337, 20.0679, -64.963196, -10.4337, 16.7232, -62.057194, -10.4337, 16.7232, -62.057194, -10.4337, 20.0679, -64.963196, -10.4337, 20.0679, -64.963196, -10.4337, 20.0679, -68.2489, -10.4337, 20.0679, -68.2489, -10.4337, 16.7232, -68.2489, -10.4337, 20.0679, -71.2999, -10.4337, 16.7232, -68.2489, -10.4337, 16.7232, -62.057194, -10.4337, 20.0679, -64.963196, -10.4337, 23.412498, -64.963196, -10.4337, 20.0679, -64.963196, -10.4337, 23.412498, -68.2489, -10.4337, 20.0679, -64.963196, -10.4337, 20.0679, -62.057194, -10.4337, 20.0679, -62.057194, -10.4337, 23.412498, -64.963196, -10.4337, 23.412498, -71.2999, -10.4337, 20.0679, -73.8239, -10.4337, 16.7232, -71.2999, -10.4337, 16.7232, -68.2489, -10.4337, 20.0679, -71.2999, -10.4337, 20.0679, -71.2999, -10.4337, 16.7232, -71.2999, -10.4337, 20.0679, -73.8239, -10.4337, 20.0679, -73.8239, -10.4337, 16.7232, -64.963196, -10.4337, 23.412498, -68.2489, -10.4337, 23.412498, -68.2489, -10.4337, 20.0679, -68.2489, -10.4337, 23.412498, -71.2999, -10.4337, 20.0679, -68.2489, -10.4337, 20.0679, -62.057194, -10.4337, 23.412498, -64.963196, -10.4337, 26.7572, -64.963196, -10.4337, 23.412498, -64.963196, -10.4337, 26.7572, -68.2489, -10.4337, 23.412498, -64.963196, -10.4337, 23.412498, -62.057194, -10.4337, 23.412498, -62.057194, -10.4337, 26.7572, -64.963196, -10.4337, 26.7572, -71.2999, -10.4337, 23.412498, -73.8239, -10.4337, 20.0679, -71.2999, -10.4337, 20.0679, -68.2489, -10.4337, 23.412498, -71.2999, -10.4337, 23.412498, -71.2999, -10.4337, 20.0679, -71.2999, -10.4337, 23.412498, -73.8239, -10.4337, 23.412498, -73.8239, -10.4337, 20.0679, -68.2489, -10.4337, 26.7572, -71.2999, -10.4337, 23.412498, -68.2489, -10.4337, 23.412498, -64.963196, -10.4337, 26.7572, -68.2489, -10.4337, 26.7572, -68.2489, -10.4337, 23.412498, -71.2999, -10.4337, 26.7572, -73.8239, -10.4337, 23.412498, -71.2999, -10.4337, 23.412498, -68.2489, -10.4337, 26.7572, -71.2999, -10.4337, 26.7572, -71.2999, -10.4337, 23.412498, -71.2999, -10.4337, 26.7572, -73.8239, -10.4337, 26.7572, -73.8239, -10.4337, 23.412498, -73.8239, -10.4337, 26.7572, -71.2999, -10.4337, 26.7572, -72.9579, -10.4337, 27.7057, -71.2999, -10.4337, 26.7572, -71.694496, -10.4337, 29.089499, -72.9579, -10.4337, 27.7057, -71.2999, -10.4337, 26.7572, -70.1271, -10.4337, 28.0285, -71.694496, -10.4337, 29.089499, -71.2999, -10.4337, 26.7572, -68.2489, -10.4337, 26.7572, -70.1271, -10.4337, 28.0285, -70.1271, -10.4337, 28.0285, -70.3329, -10.4337, 30.580898, -71.694496, -10.4337, 29.089499, -70.1271, -10.4337, 28.0285, -68.7877, -10.4337, 29.480598, -70.3329, -10.4337, 30.580898, -68.2489, -10.4337, 26.7572, -68.7877, -10.4337, 29.480598, -70.1271, -10.4337, 28.0285, -68.7877, -10.4337, 29.480598, -69.0569, -10.4337, 31.9786, -70.3329, -10.4337, 30.580898, -68.2489, -10.4337, 26.7572, -66.948, -10.4337, 28.1705, -68.7877, -10.4337, 29.480598, -68.2489, -10.4337, 26.7572, -64.963196, -10.4337, 26.7572, -66.948, -10.4337, 28.1705, -68.7877, -10.4337, 29.480598, -67.5324, -10.4337, 30.8414, -69.0569, -10.4337, 31.9786, -66.948, -10.4337, 28.1705, -67.5324, -10.4337, 30.8414, -68.7877, -10.4337, 29.480598, -67.5324, -10.4337, 30.8414, -67.618996, -10.4337, 33.553497, -69.0569, -10.4337, 31.9786, -64.963196, -10.4337, 26.7572, -65.7283, -10.4337, 29.495699, -66.948, -10.4337, 28.1705, -66.948, -10.4337, 28.1705, -65.7283, -10.4337, 29.495699, -67.5324, -10.4337, 30.8414, -67.5324, -10.4337, 30.8414, -66.0847, -10.4337, 32.410698, -67.618996, -10.4337, 33.553497, -65.7283, -10.4337, 29.495699, -66.0847, -10.4337, 32.410698, -67.5324, -10.4337, 30.8414, -66.0847, -10.4337, 32.410698, -66.2484, -10.4337, 35.0548, -67.618996, -10.4337, 33.553497, -64.963196, -10.4337, 26.7572, -63.779697, -10.4337, 28.042099, -65.7283, -10.4337, 29.495699, -64.963196, -10.4337, 26.7572, -62.057194, -10.4337, 26.7572, -63.779697, -10.4337, 28.042099, -65.7283, -10.4337, 29.495699, -64.2815, -10.4337, 31.0675, -66.0847, -10.4337, 32.410698, -63.779697, -10.4337, 28.042099, -64.2815, -10.4337, 31.0675, -65.7283, -10.4337, 29.495699, -66.0847, -10.4337, 32.410698, -64.736496, -10.4337, 33.8722, -66.2484, -10.4337, 35.0548, -64.2815, -10.4337, 31.0675, -64.736496, -10.4337, 33.8722, -66.0847, -10.4337, 32.410698, -64.736496, -10.4337, 33.8722, -65.1522, -10.4337, 36.255398, -66.2484, -10.4337, 35.0548, -62.057194, -10.4337, 26.7572, -62.331196, -10.4337, 29.6148, -63.779697, -10.4337, 28.042099, -63.779697, -10.4337, 28.042099, -62.331196, -10.4337, 29.6148, -64.2815, -10.4337, 31.0675, -62.057194, -10.4337, 26.7572, -60.611, -10.4337, 28.3335, -62.331196, -10.4337, 29.6148, -64.2815, -10.4337, 31.0675, -62.971096, -10.4337, 32.4913, -64.736496, -10.4337, 33.8722, -62.331196, -10.4337, 29.6148, -62.971096, -10.4337, 32.4913, -64.2815, -10.4337, 31.0675, -64.736496, -10.4337, 33.8722, -63.642498, -10.4337, 35.0582, -65.1522, -10.4337, 36.255398, -62.971096, -10.4337, 32.4913, -63.642498, -10.4337, 35.0582, -64.736496, -10.4337, 33.8722, -63.642498, -10.4337, 35.0582, -63.8252, -10.4337, 37.7089, -65.1522, -10.4337, 36.255398, -60.611, -10.4337, 28.3335, -61.059097, -10.4337, 30.9959, -62.331196, -10.4337, 29.6148, -62.331196, -10.4337, 29.6148, -61.059097, -10.4337, 30.9959, -62.971096, -10.4337, 32.4913, -60.611, -10.4337, 28.3335, -59.3761, -10.4337, 29.679499, -61.059097, -10.4337, 30.9959, -62.971096, -10.4337, 32.4913, -61.8887, -10.4337, 33.667297, -63.642498, -10.4337, 35.0582, -61.059097, -10.4337, 30.9959, -61.8887, -10.4337, 33.667297, -62.971096, -10.4337, 32.4913, -63.642498, -10.4337, 35.0582, -62.323597, -10.4337, 36.4879, -63.8252, -10.4337, 37.7089, -61.8887, -10.4337, 33.667297, -62.323597, -10.4337, 36.4879, -63.642498, -10.4337, 35.0582, -62.323597, -10.4337, 36.4879, -62.558193, -10.4337, 39.0967, -63.8252, -10.4337, 37.7089, -59.3761, -10.4337, 29.679499, -59.9874, -10.4337, 32.1594, -61.059097, -10.4337, 30.9959, -61.059097, -10.4337, 30.9959, -59.9874, -10.4337, 32.1594, -61.8887, -10.4337, 33.667297, -59.3761, -10.4337, 29.679499, -58.316597, -10.4337, 30.834398, -59.9874, -10.4337, 32.1594, -61.8887, -10.4337, 33.667297, -60.59, -10.4337, 35.078197, -62.323597, -10.4337, 36.4879, -59.9874, -10.4337, 32.1594, -60.59, -10.4337, 35.078197, -61.8887, -10.4337, 33.667297, -62.323597, -10.4337, 36.4879, -61.055798, -10.4337, 37.8623, -62.558193, -10.4337, 39.0967, -60.59, -10.4337, 35.078197, -61.055798, -10.4337, 37.8623, -62.323597, -10.4337, 36.4879, -61.055798, -10.4337, 37.8623, -61.1665, -10.4337, 40.621098, -62.558193, -10.4337, 39.0967, -58.316597, -10.4337, 30.834398, -58.708298, -10.4337, 33.5481, -59.9874, -10.4337, 32.1594, -59.9874, -10.4337, 32.1594, -58.708298, -10.4337, 33.5481, -60.59, -10.4337, 35.078197, -58.316597, -10.4337, 30.834398, -57.058, -10.4337, 32.2062, -58.708298, -10.4337, 33.5481, -60.59, -10.4337, 35.078197, -59.331398, -10.4337, 36.4456, -61.055798, -10.4337, 37.8623, -58.708298, -10.4337, 33.5481, -59.331398, -10.4337, 36.4456, -60.59, -10.4337, 35.078197, -61.055798, -10.4337, 37.8623, -59.6695, -10.4337, 39.3651, -61.1665, -10.4337, 40.621098, -59.331398, -10.4337, 36.4456, -59.6695, -10.4337, 39.3651, -61.055798, -10.4337, 37.8623, -59.6695, -10.4337, 39.3651, -59.815098, -10.4337, 42.1012, -61.1665, -10.4337, 40.621098, -57.058, -10.4337, 32.2062, -57.457497, -10.4337, 34.906097, -58.708298, -10.4337, 33.5481, -58.708298, -10.4337, 33.5481, -57.457497, -10.4337, 34.906097, -59.331398, -10.4337, 36.4456, -57.058, -10.4337, 32.2062, -55.8173, -10.4337, 33.5585, -57.457497, -10.4337, 34.906097, -59.331398, -10.4337, 36.4456, -57.962597, -10.4337, 37.9328, -59.6695, -10.4337, 39.3651, -57.457497, -10.4337, 34.906097, -57.962597, -10.4337, 37.9328, -59.331398, -10.4337, 36.4456, -59.6695, -10.4337, 39.3651, -58.3182, -10.4337, 40.8299, -59.815098, -10.4337, 42.1012, -57.962597, -10.4337, 37.9328, -58.3182, -10.4337, 40.8299, -59.6695, -10.4337, 39.3651, -58.3182, -10.4337, 40.8299, -58.397198, -10.4337, 43.6543, -59.815098, -10.4337, 42.1012, -55.8173, -10.4337, 33.5585, -56.105198, -10.4337, 36.374397, -57.457497, -10.4337, 34.906097, -57.457497, -10.4337, 34.906097, -56.105198, -10.4337, 36.374397, -57.962597, -10.4337, 37.9328, -55.8173, -10.4337, 33.5585, -54.4828, -10.4337, 35.0131, -56.105198, -10.4337, 36.374397, -57.962597, -10.4337, 37.9328, -56.621998, -10.4337, 39.389297, -58.3182, -10.4337, 40.8299, -56.105198, -10.4337, 36.374397, -56.621998, -10.4337, 39.389297, -57.962597, -10.4337, 37.9328, -58.3182, -10.4337, 40.8299, -56.8974, -10.4337, 42.370197, -58.397198, -10.4337, 43.6543, -56.621998, -10.4337, 39.389297, -56.8974, -10.4337, 42.370197, -58.3182, -10.4337, 40.8299, -56.8974, -10.4337, 42.370197, -57.1702, -10.4337, 44.9982, -58.397198, -10.4337, 43.6543, -54.4828, -10.4337, 35.0131, -54.7739, -10.4337, 37.819798, -56.105198, -10.4337, 36.374397, -56.105198, -10.4337, 36.374397, -54.7739, -10.4337, 37.819798, -56.621998, -10.4337, 39.389297, -54.4828, -10.4337, 35.0131, -53.163, -10.4337, 36.4517, -54.7739, -10.4337, 37.819798, -56.621998, -10.4337, 39.389297, -55.2089, -10.4337, 40.9246, -56.8974, -10.4337, 42.370197, -54.7739, -10.4337, 37.819798, -55.2089, -10.4337, 40.9246, -56.621998, -10.4337, 39.389297, -56.8974, -10.4337, 42.370197, -55.676598, -10.4337, 43.6936, -57.1702, -10.4337, 44.9982, -55.2089, -10.4337, 40.9246, -55.676598, -10.4337, 43.6936, -56.8974, -10.4337, 42.370197, -55.676598, -10.4337, 43.6936, -55.7286, -10.4337, 46.577297, -57.1702, -10.4337, 44.9982, -53.163, -10.4337, 36.4517, -53.3668, -10.4337, 39.3474, -54.7739, -10.4337, 37.819798, -54.7739, -10.4337, 37.819798, -53.3668, -10.4337, 39.3474, -55.2089, -10.4337, 40.9246, -53.163, -10.4337, 36.4517, -51.764698, -10.4337, 37.9757, -53.3668, -10.4337, 39.3474, -55.2089, -10.4337, 40.9246, -54.004498, -10.4337, 42.233097, -55.676598, -10.4337, 43.6936, -53.3668, -10.4337, 39.3474, -54.004498, -10.4337, 42.233097, -55.2089, -10.4337, 40.9246, -55.676598, -10.4337, 43.6936, -54.2416, -10.4337, 45.2492, -55.7286, -10.4337, 46.577297, -54.004498, -10.4337, 42.233097, -54.2416, -10.4337, 45.2492, -55.676598, -10.4337, 43.6936, -54.2416, -10.4337, 45.2492, -54.056297, -10.4337, 48.408897, -55.7286, -10.4337, 46.577297, -51.764698, -10.4337, 37.9757, -52.1782, -10.4337, 40.637897, -53.3668, -10.4337, 39.3474, -53.3668, -10.4337, 39.3474, -52.1782, -10.4337, 40.637897, -54.004498, -10.4337, 42.233097, -51.764698, -10.4337, 37.9757, -50.5928, -10.4337, 39.2532, -52.1782, -10.4337, 40.637897, -54.004498, -10.4337, 42.233097, -52.5879, -10.4337, 43.772198, -54.2416, -10.4337, 45.2492, -52.1782, -10.4337, 40.637897, -52.5879, -10.4337, 43.772198, -54.004498, -10.4337, 42.233097, -54.2416, -10.4337, 45.2492, -52.5793, -10.4337, 47.051098, -54.056297, -10.4337, 48.408897, -52.5879, -10.4337, 43.772198, -52.5793, -10.4337, 47.051098, -54.2416, -10.4337, 45.2492, -52.5793, -10.4337, 47.051098, -52.2215, -10.4337, 50.418697, -54.056297, -10.4337, 48.408897, -50.5928, -10.4337, 39.2532, -50.779198, -10.4337, 42.1568, -52.1782, -10.4337, 40.637897, -52.1782, -10.4337, 40.637897, -50.779198, -10.4337, 42.1568, -52.5879, -10.4337, 43.772198, -50.5928, -10.4337, 39.2532, -49.212498, -10.4337, 40.7576, -50.779198, -10.4337, 42.1568, -52.5879, -10.4337, 43.772198, -50.9492, -10.4337, 45.552597, -52.5793, -10.4337, 47.051098, -50.779198, -10.4337, 42.1568, -50.9492, -10.4337, 45.552597, -52.5879, -10.4337, 43.772198, -52.5793, -10.4337, 47.051098, -50.741398, -10.4337, 49.0436, -52.2215, -10.4337, 50.418697, -50.9492, -10.4337, 45.552597, -50.741398, -10.4337, 49.0436, -52.5793, -10.4337, 47.051098, -50.741398, -10.4337, 49.0436, -50.267597, -10.4337, 52.5587, -52.2215, -10.4337, 50.418697, -49.212498, -10.4337, 40.7576, -49.163498, -10.4337, 43.911, -50.779198, -10.4337, 42.1568, -50.779198, -10.4337, 42.1568, -49.163498, -10.4337, 43.911, -50.9492, -10.4337, 45.552597, -49.212498, -10.4337, 40.7576, -47.620697, -10.4337, 42.4927, -49.163498, -10.4337, 43.911, -50.9492, -10.4337, 45.552597, -49.1213, -10.4337, 47.5384, -50.741398, -10.4337, 49.0436, -49.163498, -10.4337, 43.911, -49.1213, -10.4337, 47.5384, -50.9492, -10.4337, 45.552597, -50.741398, -10.4337, 49.0436, -48.787197, -10.4337, 51.162, -50.267597, -10.4337, 52.5587, -49.1213, -10.4337, 47.5384, -48.787197, -10.4337, 51.162, -50.741398, -10.4337, 49.0436, -48.787197, -10.4337, 51.162, -48.3197, -10.4337, 54.6923, -50.267597, -10.4337, 52.5587, -47.620697, -10.4337, 42.4927, -47.343597, -10.4337, 45.8868, -49.163498, -10.4337, 43.911, -49.163498, -10.4337, 43.911, -47.343597, -10.4337, 45.8868, -49.1213, -10.4337, 47.5384, -47.620697, -10.4337, 42.4927, -45.8121, -10.4337, 44.463898, -47.343597, -10.4337, 45.8868, -49.1213, -10.4337, 47.5384, -47.181, -10.4337, 49.6466, -48.787197, -10.4337, 51.162, -47.343597, -10.4337, 45.8868, -47.181, -10.4337, 49.6466, -49.1213, -10.4337, 47.5384, -48.787197, -10.4337, 51.162, -46.8489, -10.4337, 53.263298, -48.3197, -10.4337, 54.6923, -47.181, -10.4337, 49.6466, -46.8489, -10.4337, 53.263298, -48.787197, -10.4337, 51.162, -46.8489, -10.4337, 53.263298, -46.839, -10.4337, 56.314198, -48.3197, -10.4337, 54.6923, -45.8121, -10.4337, 44.463898, -45.4151, -10.4337, 47.9805, -47.343597, -10.4337, 45.8868, -47.343597, -10.4337, 45.8868, -45.4151, -10.4337, 47.9805, -47.181, -10.4337, 49.6466, -45.8121, -10.4337, 44.463898, -43.8985, -10.4337, 46.549698, -45.4151, -10.4337, 47.9805, -47.181, -10.4337, 49.6466, -45.2669, -10.4337, 51.7262, -46.8489, -10.4337, 53.263298, -45.4151, -10.4337, 47.9805, -45.2669, -10.4337, 51.7262, -47.181, -10.4337, 49.6466, -46.8489, -10.4337, 53.263298, -45.3659, -10.4337, 54.8708, -46.839, -10.4337, 56.314198, -45.2669, -10.4337, 51.7262, -45.3659, -10.4337, 54.8708, -46.8489, -10.4337, 53.263298, -45.3659, -10.4337, 54.8708, -45.411, -10.4337, 57.8783, -46.839, -10.4337, 56.314198, -43.8985, -10.4337, 46.549698, -43.524498, -10.4337, 50.0333, -45.4151, -10.4337, 47.9805, -45.4151, -10.4337, 47.9805, -43.524498, -10.4337, 50.0333, -45.2669, -10.4337, 51.7262, -43.8985, -10.4337, 46.549698, -42.032497, -10.4337, 48.5836, -43.524498, -10.4337, 50.0333, -45.2669, -10.4337, 51.7262, -43.792, -10.4337, 53.328598, -45.3659, -10.4337, 54.8708, -43.524498, -10.4337, 50.0333, -43.792, -10.4337, 53.328598, -45.2669, -10.4337, 51.7262, -45.3659, -10.4337, 54.8708, -43.935497, -10.4337, 56.421497, -45.411, -10.4337, 57.8783, -43.792, -10.4337, 53.328598, -43.935497, -10.4337, 56.421497, -45.3659, -10.4337, 54.8708, -43.935497, -10.4337, 56.421497, -43.670998, -10.4337, 59.7842, -45.411, -10.4337, 57.8783, -42.032497, -10.4337, 48.5836, -42.056, -10.4337, 51.6275, -43.524498, -10.4337, 50.0333, -43.524498, -10.4337, 50.0333, -42.056, -10.4337, 51.6275, -43.792, -10.4337, 53.328598, -42.032497, -10.4337, 48.5836, -40.572998, -10.4337, 50.1744, -42.056, -10.4337, 51.6275, -43.792, -10.4337, 53.328598, -42.368797, -10.4337, 54.874798, -43.935497, -10.4337, 56.421497, -42.056, -10.4337, 51.6275, -42.368797, -10.4337, 54.874798, -43.792, -10.4337, 53.328598, -43.935497, -10.4337, 56.421497, -42.2006, -10.4337, 58.3022, -43.670998, -10.4337, 59.7842, -42.368797, -10.4337, 54.874798, -42.2006, -10.4337, 58.3022, -43.935497, -10.4337, 56.421497, -42.2006, -10.4337, 58.3022, -41.9354, -10.4337, 61.685097, -43.670998, -10.4337, 59.7842, -40.572998, -10.4337, 50.1744, -40.6386, -10.4337, 53.1665, -42.056, -10.4337, 51.6275, -42.056, -10.4337, 51.6275, -40.6386, -10.4337, 53.1665, -42.368797, -10.4337, 54.874798, -40.572998, -10.4337, 50.1744, -39.1638, -10.4337, 51.7104, -40.6386, -10.4337, 53.1665, -42.368797, -10.4337, 54.874798, -40.6512, -10.4337, 56.7408, -42.2006, -10.4337, 58.3022, -40.6386, -10.4337, 53.1665, -40.6512, -10.4337, 56.7408, -42.368797, -10.4337, 54.874798, -42.2006, -10.4337, 58.3022, -40.4544, -10.4337, 60.1951, -41.9354, -10.4337, 61.685097, -40.6512, -10.4337, 56.7408, -40.4544, -10.4337, 60.1951, -42.2006, -10.4337, 58.3022, -40.4544, -10.4337, 60.1951, -40.3009, -10.4337, 63.4754, -41.9354, -10.4337, 61.685097, -39.1638, -10.4337, 51.7104, -38.937298, -10.4337, 55.0135, -40.6386, -10.4337, 53.1665, -40.6386, -10.4337, 53.1665, -38.937298, -10.4337, 55.0135, -40.6512, -10.4337, 56.7408, -39.1638, -10.4337, 51.7104, -37.4804, -10.4337, 53.5453, -38.937298, -10.4337, 55.0135, -40.6512, -10.4337, 56.7408, -38.9059, -10.4337, 58.6371, -40.4544, -10.4337, 60.1951, -38.937298, -10.4337, 55.0135, -38.9059, -10.4337, 58.6371, -40.6512, -10.4337, 56.7408, -40.4544, -10.4337, 60.1951, -38.822998, -10.4337, 61.9637, -40.3009, -10.4337, 63.4754, -38.9059, -10.4337, 58.6371, -38.822998, -10.4337, 61.9637, -40.4544, -10.4337, 60.1951, -38.822998, -10.4337, 61.9637, -38.953697, -10.4337, 64.951, -40.3009, -10.4337, 63.4754, -37.4804, -10.4337, 53.5453, -37.1899, -10.4337, 56.910698, -38.937298, -10.4337, 55.0135, -38.937298, -10.4337, 55.0135, -37.1899, -10.4337, 56.910698, -38.9059, -10.4337, 58.6371, -37.4804, -10.4337, 53.5453, -35.735397, -10.4337, 55.4473, -37.1899, -10.4337, 56.910698, -38.9059, -10.4337, 58.6371, -37.2887, -10.4337, 60.3941, -38.822998, -10.4337, 61.9637, -37.1899, -10.4337, 56.910698, -37.2887, -10.4337, 60.3941, -38.9059, -10.4337, 58.6371, -38.822998, -10.4337, 61.9637, -37.4867, -10.4337, 63.4123, -38.953697, -10.4337, 64.951, -37.2887, -10.4337, 60.3941, -37.4867, -10.4337, 63.4123, -38.822998, -10.4337, 61.9637, -37.4867, -10.4337, 63.4123, -37.4493, -10.4337, 66.5989, -38.953697, -10.4337, 64.951, -35.735397, -10.4337, 55.4473, -35.5858, -10.4337, 58.6522, -37.1899, -10.4337, 56.910698, -37.1899, -10.4337, 56.910698, -35.5858, -10.4337, 58.6522, -37.2887, -10.4337, 60.3941, -35.735397, -10.4337, 55.4473, -34.1461, -10.4337, 57.1796, -35.5858, -10.4337, 58.6522, -37.2887, -10.4337, 60.3941, -35.9725, -10.4337, 61.824097, -37.4867, -10.4337, 63.4123, -35.5858, -10.4337, 58.6522, -35.9725, -10.4337, 61.824097, -37.2887, -10.4337, 60.3941, -37.4867, -10.4337, 63.4123, -35.9927, -10.4337, 65.0319, -37.4493, -10.4337, 66.5989, -35.9725, -10.4337, 61.824097, -35.9927, -10.4337, 65.0319, -37.4867, -10.4337, 63.4123, -35.9927, -10.4337, 65.0319, -36.0228, -10.4337, 68.1614, -37.4493, -10.4337, 66.5989, -34.1461, -10.4337, 57.1796, -34.2898, -10.4337, 60.0593, -35.5858, -10.4337, 58.6522, -35.5858, -10.4337, 58.6522, -34.2898, -10.4337, 60.0593, -35.9725, -10.4337, 61.824097, -34.1461, -10.4337, 57.1796, -32.870197, -10.4337, 58.570297, -34.2898, -10.4337, 60.0593, -35.9725, -10.4337, 61.824097, -34.499, -10.4337, 63.425, -35.9927, -10.4337, 65.0319, -34.2898, -10.4337, 60.0593, -34.499, -10.4337, 63.425, -35.9725, -10.4337, 61.824097, -35.9927, -10.4337, 65.0319, -34.5678, -10.4337, 66.5766, -36.0228, -10.4337, 68.1614, -34.499, -10.4337, 63.425, -34.5678, -10.4337, 66.5766, -35.9927, -10.4337, 65.0319, -34.5678, -10.4337, 66.5766, -34.1187, -10.4337, 70.246895, -36.0228, -10.4337, 68.1614, -32.870197, -10.4337, 58.570297, -32.8368, -10.4337, 61.636898, -34.2898, -10.4337, 60.0593, -34.2898, -10.4337, 60.0593, -32.8368, -10.4337, 61.636898, -34.499, -10.4337, 63.425, -32.870197, -10.4337, 58.570297, -31.437601, -10.4337, 60.131798, -32.8368, -10.4337, 61.636898, -34.499, -10.4337, 63.425, -33.0849, -10.4337, 64.961395, -34.5678, -10.4337, 66.5766, -32.8368, -10.4337, 61.636898, -33.0849, -10.4337, 64.961395, -34.499, -10.4337, 63.425, -34.5678, -10.4337, 66.5766, -32.69, -10.4337, 68.6121, -34.1187, -10.4337, 70.246895, -33.0849, -10.4337, 64.961395, -32.69, -10.4337, 68.6121, -34.5678, -10.4337, 66.5766, -32.69, -10.4337, 68.6121, -32.6527, -10.4337, 71.8527, -34.1187, -10.4337, 70.246895, -31.437601, -10.4337, 60.131798, -31.4325, -10.4337, 63.1615, -32.8368, -10.4337, 61.636898, -32.8368, -10.4337, 61.636898, -31.4325, -10.4337, 63.1615, -33.0849, -10.4337, 64.961395, -31.437601, -10.4337, 60.131798, -30.044802, -10.4337, 61.649998, -31.4325, -10.4337, 63.1615, -33.0849, -10.4337, 64.961395, -31.245802, -10.4337, 66.959496, -32.69, -10.4337, 68.6121, -31.4325, -10.4337, 63.1615, -31.245802, -10.4337, 66.959496, -33.0849, -10.4337, 64.961395, -32.69, -10.4337, 68.6121, -31.2026, -10.4337, 70.2246, -32.6527, -10.4337, 71.8527, -31.245802, -10.4337, 66.959496, -31.2026, -10.4337, 70.2246, -32.69, -10.4337, 68.6121, -31.2026, -10.4337, 70.2246, -31.218801, -10.4337, 73.423195, -32.6527, -10.4337, 71.8527, -30.044802, -10.4337, 61.649998, -29.633501, -10.4337, 65.1147, -31.4325, -10.4337, 63.1615, -31.4325, -10.4337, 63.1615, -29.633501, -10.4337, 65.1147, -31.245802, -10.4337, 66.959496, -30.044802, -10.4337, 61.649998, -28.2834, -10.4337, 63.569798, -29.633501, -10.4337, 65.1147, -31.245802, -10.4337, 66.959496, -29.745901, -10.4337, 68.589, -31.2026, -10.4337, 70.2246, -29.633501, -10.4337, 65.1147, -29.745901, -10.4337, 68.589, -31.245802, -10.4337, 66.959496, -31.2026, -10.4337, 70.2246, -29.787, -10.4337, 71.7591, -31.218801, -10.4337, 73.423195, -29.745901, -10.4337, 68.589, -29.787, -10.4337, 71.7591, -31.2026, -10.4337, 70.2246, -29.787, -10.4337, 71.7591, -29.6429, -10.4337, 75.1494, -31.218801, -10.4337, 73.423195, -28.2834, -10.4337, 63.569798, -28.117498, -10.4337, 66.7606, -29.633501, -10.4337, 65.1147, -29.633501, -10.4337, 65.1147, -28.117498, -10.4337, 66.7606, -29.745901, -10.4337, 68.589, -28.2834, -10.4337, 63.569798, -26.7572, -10.4337, 65.2334, -28.117498, -10.4337, 66.7606, -29.745901, -10.4337, 68.589, -28.357399, -10.4337, 70.097496, -29.787, -10.4337, 71.7591, -28.117498, -10.4337, 66.7606, -28.357399, -10.4337, 70.097496, -29.745901, -10.4337, 68.589, -29.787, -10.4337, 71.7591, -28.1941, -10.4337, 73.4859, -29.6429, -10.4337, 75.1494, -28.357399, -10.4337, 70.097496, -28.1941, -10.4337, 73.4859, -29.787, -10.4337, 71.7591, -28.1941, -10.4337, 73.4859, -28.2134, -10.4337, 76.715096, -29.6429, -10.4337, 75.1494, -26.7572, -10.4337, 65.2334, -26.7572, -10.4337, 68.237495, -28.117498, -10.4337, 66.7606, -28.117498, -10.4337, 66.7606, -26.7572, -10.4337, 68.237495, -28.357399, -10.4337, 70.097496, -26.7572, -10.4337, 68.237495, -26.7572, -10.4337, 65.2334, -23.412498, -10.4337, 65.2334, -28.1941, -10.4337, 73.4859, -26.7572, -10.4337, 75.043594, -28.2134, -10.4337, 76.715096, -26.7572, -10.4337, 75.043594, -26.7572, -10.4337, 78.3101, -28.2134, -10.4337, 76.715096, -28.357399, -10.4337, 70.097496, -26.7572, -10.4337, 71.8361, -28.1941, -10.4337, 73.4859, -26.7572, -10.4337, 68.237495, -26.7572, -10.4337, 71.8361, -28.357399, -10.4337, 70.097496, -26.7572, -10.4337, 71.8361, -26.7572, -10.4337, 75.043594, -28.1941, -10.4337, 73.4859, -23.412498, -10.4337, 78.3101, -26.7572, -10.4337, 78.3101, -26.7572, -10.4337, 75.043594, -26.7572, -10.4337, 71.8361, -26.7572, -10.4337, 68.237495, -23.412498, -10.4337, 68.237495, -26.7572, -10.4337, 68.237495, -23.412498, -10.4337, 65.2334, -23.412498, -10.4337, 68.237495, -23.412498, -10.4337, 68.237495, -23.412498, -10.4337, 65.2334, -20.0679, -10.4337, 65.2334, -26.7572, -10.4337, 71.8361, -23.412498, -10.4337, 68.237495, -23.412498, -10.4337, 71.8361, -26.7572, -10.4337, 75.043594, -26.7572, -10.4337, 71.8361, -23.412498, -10.4337, 71.8361, -23.412498, -10.4337, 68.237495, -20.0679, -10.4337, 65.2334, -20.0679, -10.4337, 68.237495, -23.412498, -10.4337, 71.8361, -23.412498, -10.4337, 68.237495, -20.0679, -10.4337, 68.237495, -16.7232, -10.4337, 65.2334, -20.0679, -10.4337, 68.237495, -20.0679, -10.4337, 65.2334, -23.412498, -10.4337, 78.3101, -26.7572, -10.4337, 75.043594, -23.412498, -10.4337, 75.043594, -26.7572, -10.4337, 75.043594, -23.412498, -10.4337, 71.8361, -23.412498, -10.4337, 75.043594, -20.0679, -10.4337, 78.3101, -23.412498, -10.4337, 78.3101, -23.412498, -10.4337, 75.043594, -23.412498, -10.4337, 71.8361, -20.0679, -10.4337, 68.237495, -20.0679, -10.4337, 71.8361, -23.412498, -10.4337, 75.043594, -23.412498, -10.4337, 71.8361, -20.0679, -10.4337, 71.8361, -16.7232, -10.4337, 65.2334, -16.7232, -10.4337, 68.237495, -20.0679, -10.4337, 68.237495, -16.7232, -10.4337, 68.237495, -20.0679, -10.4337, 71.8361, -20.0679, -10.4337, 68.237495, -16.7232, -10.4337, 68.237495, -16.7232, -10.4337, 65.2334, -13.378601, -10.4337, 65.2334, -23.412498, -10.4337, 75.043594, -20.0679, -10.4337, 71.8361, -20.0679, -10.4337, 75.043594, -20.0679, -10.4337, 78.3101, -23.412498, -10.4337, 75.043594, -20.0679, -10.4337, 75.043594, -16.7232, -10.4337, 68.237495, -16.7232, -10.4337, 71.8361, -20.0679, -10.4337, 71.8361, -16.7232, -10.4337, 71.8361, -20.0679, -10.4337, 75.043594, -20.0679, -10.4337, 71.8361, -16.7232, -10.4337, 75.043594, -20.0679, -10.4337, 78.3101, -20.0679, -10.4337, 75.043594, -16.7232, -10.4337, 71.8361, -16.7232, -10.4337, 75.043594, -20.0679, -10.4337, 75.043594, -16.7232, -10.4337, 75.043594, -16.7232, -10.4337, 78.3101, -20.0679, -10.4337, 78.3101, -16.7232, -10.4337, 71.8361, -16.7232, -10.4337, 68.237495, -13.378601, -10.4337, 68.237495, -16.7232, -10.4337, 68.237495, -13.378601, -10.4337, 65.2334, -13.378601, -10.4337, 68.237495, -16.7232, -10.4337, 75.043594, -16.7232, -10.4337, 71.8361, -13.378601, -10.4337, 71.8361, -16.7232, -10.4337, 71.8361, -13.378601, -10.4337, 68.237495, -13.378601, -10.4337, 71.8361, -16.7232, -10.4337, 78.3101, -16.7232, -10.4337, 75.043594, -13.378601, -10.4337, 75.043594, -16.7232, -10.4337, 75.043594, -13.378601, -10.4337, 71.8361, -13.378601, -10.4337, 75.043594, -16.7232, -10.4337, 78.3101, -13.378601, -10.4337, 75.043594, -13.378601, -10.4337, 78.3101, -13.378601, -10.4337, 65.2334, -10.033899, -10.4337, 68.237495, -13.378601, -10.4337, 68.237495, -10.033899, -10.4337, 68.237495, -13.378601, -10.4337, 71.8361, -13.378601, -10.4337, 68.237495, -13.378601, -10.4337, 65.2334, -10.033899, -10.4337, 65.2334, -10.033899, -10.4337, 68.237495, -10.033899, -10.4337, 71.8361, -13.378601, -10.4337, 75.043594, -13.378601, -10.4337, 71.8361, -10.033899, -10.4337, 68.237495, -10.033899, -10.4337, 71.8361, -13.378601, -10.4337, 71.8361, -10.033899, -10.4337, 75.043594, -13.378601, -10.4337, 78.3101, -13.378601, -10.4337, 75.043594, -10.033899, -10.4337, 71.8361, -10.033899, -10.4337, 75.043594, -13.378601, -10.4337, 75.043594, -10.033899, -10.4337, 75.043594, -10.033899, -10.4337, 78.3101, -13.378601, -10.4337, 78.3101, -10.033899, -10.4337, 65.2334, -6.6893, -10.4337, 68.237495, -10.033899, -10.4337, 68.237495, -6.6893, -10.4337, 68.237495, -10.033899, -10.4337, 71.8361, -10.033899, -10.4337, 68.237495, -10.033899, -10.4337, 65.2334, -6.6893, -10.4337, 65.2334, -6.6893, -10.4337, 68.237495, -6.6893, -10.4337, 71.8361, -10.033899, -10.4337, 75.043594, -10.033899, -10.4337, 71.8361, -6.6893, -10.4337, 68.237495, -6.6893, -10.4337, 71.8361, -10.033899, -10.4337, 71.8361, -6.6893, -10.4337, 75.043594, -10.033899, -10.4337, 78.3101, -10.033899, -10.4337, 75.043594, -6.6893, -10.4337, 71.8361, -6.6893, -10.4337, 75.043594, -10.033899, -10.4337, 75.043594, -6.6893, -10.4337, 75.043594, -6.6893, -10.4337, 78.3101, -10.033899, -10.4337, 78.3101, -6.6893, -10.4337, 65.2334, -3.3446002, -10.4337, 68.237495, -6.6893, -10.4337, 68.237495, -3.3446002, -10.4337, 68.237495, -6.6893, -10.4337, 71.8361, -6.6893, -10.4337, 68.237495, -6.6893, -10.4337, 65.2334, -3.3446002, -10.4337, 65.2334, -3.3446002, -10.4337, 68.237495, -3.3446002, -10.4337, 71.8361, -6.6893, -10.4337, 75.043594, -6.6893, -10.4337, 71.8361, -3.3446002, -10.4337, 68.237495, -3.3446002, -10.4337, 71.8361, -6.6893, -10.4337, 71.8361, -3.3446002, -10.4337, 75.043594, -6.6893, -10.4337, 78.3101, -6.6893, -10.4337, 75.043594, -3.3446002, -10.4337, 71.8361, -3.3446002, -10.4337, 75.043594, -6.6893, -10.4337, 75.043594, -3.3446002, -10.4337, 75.043594, -3.3446002, -10.4337, 78.3101, -6.6893, -10.4337, 78.3101, -3.3446002, -10.4337, 65.2334, 0, -10.4337, 68.237495, -3.3446002, -10.4337, 68.237495, 0, -10.4337, 68.237495, -3.3446002, -10.4337, 71.8361, -3.3446002, -10.4337, 68.237495, -3.3446002, -10.4337, 65.2334, 0, -10.4337, 65.2334, 0, -10.4337, 68.237495, 0, -10.4337, 71.8361, -3.3446002, -10.4337, 75.043594, -3.3446002, -10.4337, 71.8361, 0, -10.4337, 68.237495, 0, -10.4337, 71.8361, -3.3446002, -10.4337, 71.8361, 0, -10.4337, 75.043594, -3.3446002, -10.4337, 78.3101, -3.3446002, -10.4337, 75.043594, 0, -10.4337, 71.8361, 0, -10.4337, 75.043594, -3.3446002, -10.4337, 75.043594, 0, -10.4337, 75.043594, 0, -10.4337, 78.3101, -3.3446002, -10.4337, 78.3101, 0, -10.4337, 65.2334, 3.3446, -10.4337, 68.237495, 0, -10.4337, 68.237495, 3.3446, -10.4337, 68.237495, 0, -10.4337, 71.8361, 0, -10.4337, 68.237495, 0, -10.4337, 65.2334, 3.3446, -10.4337, 65.2334, 3.3446, -10.4337, 68.237495, 3.3446, -10.4337, 71.8361, 0, -10.4337, 75.043594, 0, -10.4337, 71.8361, 3.3446, -10.4337, 68.237495, 3.3446, -10.4337, 71.8361, 0, -10.4337, 71.8361, 3.3446, -10.4337, 75.043594, 0, -10.4337, 78.3101, 0, -10.4337, 75.043594, 3.3446, -10.4337, 71.8361, 3.3446, -10.4337, 75.043594, 0, -10.4337, 75.043594, 3.3446, -10.4337, 75.043594, 3.3446, -10.4337, 78.3101, 0, -10.4337, 78.3101, 3.3446, -10.4337, 65.2334, 6.6893, -10.4337, 68.237495, 3.3446, -10.4337, 68.237495, 6.6893, -10.4337, 68.237495, 3.3446, -10.4337, 71.8361, 3.3446, -10.4337, 68.237495, 3.3446, -10.4337, 65.2334, 6.6893, -10.4337, 65.2334, 6.6893, -10.4337, 68.237495, 6.6893, -10.4337, 71.8361, 3.3446, -10.4337, 75.043594, 3.3446, -10.4337, 71.8361, 6.6893, -10.4337, 68.237495, 6.6893, -10.4337, 71.8361, 3.3446, -10.4337, 71.8361, 6.6893, -10.4337, 75.043594, 3.3446, -10.4337, 78.3101, 3.3446, -10.4337, 75.043594, 6.6893, -10.4337, 71.8361, 6.6893, -10.4337, 75.043594, 3.3446, -10.4337, 75.043594, 6.6893, -10.4337, 75.043594, 6.6893, -10.4337, 78.3101, 3.3446, -10.4337, 78.3101, 6.6893, -10.4337, 65.2334, 10.033899, -10.4337, 68.237495, 6.6893, -10.4337, 68.237495, 10.033899, -10.4337, 68.237495, 6.6893, -10.4337, 71.8361, 6.6893, -10.4337, 68.237495, 6.6893, -10.4337, 65.2334, 10.033899, -10.4337, 65.2334, 10.033899, -10.4337, 68.237495, 10.033899, -10.4337, 71.8361, 6.6893, -10.4337, 75.043594, 6.6893, -10.4337, 71.8361, 10.033899, -10.4337, 68.237495, 10.033899, -10.4337, 71.8361, 6.6893, -10.4337, 71.8361, 10.033899, -10.4337, 75.043594, 6.6893, -10.4337, 78.3101, 6.6893, -10.4337, 75.043594, 10.033899, -10.4337, 71.8361, 10.033899, -10.4337, 75.043594, 6.6893, -10.4337, 75.043594, 10.033899, -10.4337, 75.043594, 10.033899, -10.4337, 78.3101, 6.6893, -10.4337, 78.3101, 10.033899, -10.4337, 65.2334, 13.378599, -10.4337, 68.237495, 10.033899, -10.4337, 68.237495, 13.378599, -10.4337, 68.237495, 10.033899, -10.4337, 71.8361, 10.033899, -10.4337, 68.237495, 10.033899, -10.4337, 65.2334, 13.378599, -10.4337, 65.2334, 13.378599, -10.4337, 68.237495, 13.378599, -10.4337, 71.8361, 10.033899, -10.4337, 75.043594, 10.033899, -10.4337, 71.8361, 13.378599, -10.4337, 68.237495, 13.378599, -10.4337, 71.8361, 10.033899, -10.4337, 71.8361, 13.378599, -10.4337, 75.043594, 10.033899, -10.4337, 78.3101, 10.033899, -10.4337, 75.043594, 13.378599, -10.4337, 71.8361, 13.378599, -10.4337, 75.043594, 10.033899, -10.4337, 75.043594, 13.378599, -10.4337, 75.043594, 13.378599, -10.4337, 78.3101, 10.033899, -10.4337, 78.3101, 13.378599, -10.4337, 65.2334, 16.7232, -10.4337, 68.237495, 13.378599, -10.4337, 68.237495, 16.7232, -10.4337, 68.237495, 13.378599, -10.4337, 71.8361, 13.378599, -10.4337, 68.237495, 13.378599, -10.4337, 65.2334, 16.7232, -10.4337, 65.2334, 16.7232, -10.4337, 68.237495, 16.7232, -10.4337, 71.8361, 13.378599, -10.4337, 75.043594, 13.378599, -10.4337, 71.8361, 16.7232, -10.4337, 68.237495, 16.7232, -10.4337, 71.8361, 13.378599, -10.4337, 71.8361, 16.7232, -10.4337, 75.043594, 13.378599, -10.4337, 78.3101, 13.378599, -10.4337, 75.043594, 16.7232, -10.4337, 71.8361, 16.7232, -10.4337, 75.043594, 13.378599, -10.4337, 75.043594, 16.7232, -10.4337, 75.043594, 16.7232, -10.4337, 78.3101, 13.378599, -10.4337, 78.3101, 16.7232, -10.4337, 65.2334, 20.0679, -10.4337, 68.237495, 16.7232, -10.4337, 68.237495, 20.0679, -10.4337, 68.237495, 16.7232, -10.4337, 71.8361, 16.7232, -10.4337, 68.237495, 16.7232, -10.4337, 65.2334, 20.0679, -10.4337, 65.2334, 20.0679, -10.4337, 68.237495, 20.0679, -10.4337, 71.8361, 16.7232, -10.4337, 75.043594, 16.7232, -10.4337, 71.8361, 20.0679, -10.4337, 68.237495, 20.0679, -10.4337, 71.8361, 16.7232, -10.4337, 71.8361, 20.0679, -10.4337, 75.043594, 16.7232, -10.4337, 78.3101, 16.7232, -10.4337, 75.043594, 20.0679, -10.4337, 71.8361, 20.0679, -10.4337, 75.043594, 16.7232, -10.4337, 75.043594, 20.0679, -10.4337, 75.043594, 20.0679, -10.4337, 78.3101, 16.7232, -10.4337, 78.3101, 20.0679, -10.4337, 65.2334, 23.412498, -10.4337, 68.237495, 20.0679, -10.4337, 68.237495, 23.412498, -10.4337, 68.237495, 20.0679, -10.4337, 71.8361, 20.0679, -10.4337, 68.237495, 20.0679, -10.4337, 65.2334, 23.412498, -10.4337, 65.2334, 23.412498, -10.4337, 68.237495, 23.412498, -10.4337, 71.8361, 20.0679, -10.4337, 75.043594, 20.0679, -10.4337, 71.8361, 23.412498, -10.4337, 68.237495, 23.412498, -10.4337, 71.8361, 20.0679, -10.4337, 71.8361, 23.412498, -10.4337, 75.043594, 20.0679, -10.4337, 78.3101, 20.0679, -10.4337, 75.043594, 23.412498, -10.4337, 71.8361, 23.412498, -10.4337, 75.043594, 20.0679, -10.4337, 75.043594, 23.412498, -10.4337, 75.043594, 23.412498, -10.4337, 78.3101, 20.0679, -10.4337, 78.3101, 23.412498, -10.4337, 65.2334, 26.7582, -10.4337, 68.237495, 23.412498, -10.4337, 68.237495, 26.7582, -10.4337, 68.237495, 23.412498, -10.4337, 71.8361, 23.412498, -10.4337, 68.237495, 23.412498, -10.4337, 65.2334, 26.7582, -10.4337, 65.2334, 26.7582, -10.4337, 68.237495, 26.7582, -10.4337, 71.8361, 23.412498, -10.4337, 75.043594, 23.412498, -10.4337, 71.8361, 26.7582, -10.4337, 68.237495, 26.7582, -10.4337, 71.8361, 23.412498, -10.4337, 71.8361, 26.7582, -10.4337, 75.043594, 23.412498, -10.4337, 78.3101, 23.412498, -10.4337, 75.043594, 26.7582, -10.4337, 71.8361, 26.7582, -10.4337, 75.043594, 23.412498, -10.4337, 75.043594, 26.7582, -10.4337, 75.043594, 26.7582, -10.4337, 78.3101, 23.412498, -10.4337, 78.3101, 26.7582, -10.4337, 75.043594, 75.8818, -10.4337, 26.7572, 26.7582, -10.4337, 78.3101, 26.7582, -10.4337, 71.8361, 75.8818, -10.4337, 26.7572, 26.7582, -10.4337, 75.043594, 26.7582, -10.4337, 78.3101, 75.8818, -10.4337, 26.7572, 79.236, -10.4337, 26.7572, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 26.7572, 75.8818, -10.4337, 26.7572, 71.9379, -10.4337, 26.7572, 26.7582, -10.4337, 71.8361, 26.7582, -10.4337, 68.237495, 75.8818, -10.4337, 26.7572, 26.7582, -10.4337, 71.8361, 71.9379, -10.4337, 26.7572, 68.9355, -10.4337, 26.7572, 26.7582, -10.4337, 68.237495, 26.7582, -10.4337, 65.2334, 71.9379, -10.4337, 26.7572, 26.7582, -10.4337, 68.237495, 68.9355, -10.4337, 26.7572, 68.9355, -10.4337, 26.7572, 26.7582, -10.4337, 65.2334, 66.1166, -10.4337, 26.7572, 68.9355, -10.4337, 26.7572, 66.1166, -10.4337, 26.7572, 66.1166, -10.4337, 23.412498, 68.9355, -10.4337, 26.7572, 66.1166, -10.4337, 23.412498, 68.937, -10.4337, 23.412498, 71.9379, -10.4337, 26.7572, 68.9355, -10.4337, 26.7572, 68.937, -10.4337, 23.412498, 68.937, -10.4337, 23.412498, 66.1166, -10.4337, 23.412498, 66.1166, -10.4337, 20.0679, 75.8818, -10.4337, 26.7572, 71.9379, -10.4337, 26.7572, 71.9555, -10.4337, 23.412498, 71.9379, -10.4337, 26.7572, 68.937, -10.4337, 23.412498, 71.9555, -10.4337, 23.412498, 79.236, -10.4337, 23.412498, 75.8818, -10.4337, 26.7572, 75.8708, -10.4337, 23.412498, 75.8818, -10.4337, 26.7572, 71.9555, -10.4337, 23.412498, 75.8708, -10.4337, 23.412498, 79.236, -10.4337, 20.0679, 79.236, -10.4337, 23.412498, 75.8708, -10.4337, 23.412498, 68.937, -10.4337, 23.412498, 66.1166, -10.4337, 20.0679, 68.9385, -10.4337, 20.0679, 71.9555, -10.4337, 23.412498, 68.937, -10.4337, 23.412498, 68.9385, -10.4337, 20.0679, 68.9385, -10.4337, 20.0679, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 16.7232, 75.8708, -10.4337, 23.412498, 71.9555, -10.4337, 23.412498, 71.9732, -10.4337, 20.0679, 71.9555, -10.4337, 23.412498, 68.9385, -10.4337, 20.0679, 71.9732, -10.4337, 20.0679, 79.236, -10.4337, 20.0679, 75.8708, -10.4337, 23.412498, 75.859795, -10.4337, 20.0679, 75.8708, -10.4337, 23.412498, 71.9732, -10.4337, 20.0679, 75.859795, -10.4337, 20.0679, 79.236, -10.4337, 16.7232, 79.236, -10.4337, 20.0679, 75.859795, -10.4337, 20.0679, 68.9385, -10.4337, 20.0679, 66.1166, -10.4337, 16.7232, 68.9401, -10.4337, 16.7232, 71.9732, -10.4337, 20.0679, 68.9385, -10.4337, 20.0679, 68.9401, -10.4337, 16.7232, 68.9401, -10.4337, 16.7232, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 13.378601, 75.859795, -10.4337, 20.0679, 71.9732, -10.4337, 20.0679, 71.9908, -10.4337, 16.7232, 71.9732, -10.4337, 20.0679, 68.9401, -10.4337, 16.7232, 71.9908, -10.4337, 16.7232, 79.236, -10.4337, 16.7232, 75.859795, -10.4337, 20.0679, 75.8488, -10.4337, 16.7232, 75.859795, -10.4337, 20.0679, 71.9908, -10.4337, 16.7232, 75.8488, -10.4337, 16.7232, 79.236, -10.4337, 13.378601, 79.236, -10.4337, 16.7232, 75.8488, -10.4337, 16.7232, 68.9401, -10.4337, 16.7232, 66.1166, -10.4337, 13.378601, 68.9416, -10.4337, 13.378601, 71.9908, -10.4337, 16.7232, 68.9401, -10.4337, 16.7232, 68.9416, -10.4337, 13.378601, 68.9416, -10.4337, 13.378601, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 10.033899, 75.8488, -10.4337, 16.7232, 71.9908, -10.4337, 16.7232, 72.0085, -10.4337, 13.378601, 71.9908, -10.4337, 16.7232, 68.9416, -10.4337, 13.378601, 72.0085, -10.4337, 13.378601, 79.236, -10.4337, 13.378601, 75.8488, -10.4337, 16.7232, 75.8377, -10.4337, 13.378601, 75.8488, -10.4337, 16.7232, 72.0085, -10.4337, 13.378601, 75.8377, -10.4337, 13.378601, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 13.378601, 75.8377, -10.4337, 13.378601, 68.9416, -10.4337, 13.378601, 66.1166, -10.4337, 10.033899, 68.9432, -10.4337, 10.033899, 72.0085, -10.4337, 13.378601, 68.9416, -10.4337, 13.378601, 68.9432, -10.4337, 10.033899, 68.9432, -10.4337, 10.033899, 66.1166, -10.4337, 10.033899, 66.1166, -10.4337, 6.6893, 75.8377, -10.4337, 13.378601, 72.0085, -10.4337, 13.378601, 72.0261, -10.4337, 10.033899, 72.0085, -10.4337, 13.378601, 68.9432, -10.4337, 10.033899, 72.0261, -10.4337, 10.033899, 79.236, -10.4337, 10.033899, 75.8377, -10.4337, 13.378601, 75.8267, -10.4337, 10.033899, 75.8377, -10.4337, 13.378601, 72.0261, -10.4337, 10.033899, 75.8267, -10.4337, 10.033899, 79.236, -10.4337, 6.6893, 79.236, -10.4337, 10.033899, 75.8267, -10.4337, 10.033899, 68.9432, -10.4337, 10.033899, 66.1166, -10.4337, 6.6893, 68.944695, -10.4337, 6.6893, 72.0261, -10.4337, 10.033899, 68.9432, -10.4337, 10.033899, 68.944695, -10.4337, 6.6893, 68.944695, -10.4337, 6.6893, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 3.3446, 75.8267, -10.4337, 10.033899, 72.0261, -10.4337, 10.033899, 72.0437, -10.4337, 6.6893, 72.0261, -10.4337, 10.033899, 68.944695, -10.4337, 6.6893, 72.0437, -10.4337, 6.6893, 79.236, -10.4337, 6.6893, 75.8267, -10.4337, 10.033899, 75.8157, -10.4337, 6.6893, 75.8267, -10.4337, 10.033899, 72.0437, -10.4337, 6.6893, 75.8157, -10.4337, 6.6893, 79.236, -10.4337, 3.3446, 79.236, -10.4337, 6.6893, 75.8157, -10.4337, 6.6893, 68.944695, -10.4337, 6.6893, 66.1166, -10.4337, 3.3446, 68.9463, -10.4337, 3.3446, 72.0437, -10.4337, 6.6893, 68.944695, -10.4337, 6.6893, 68.9463, -10.4337, 3.3446, 68.9463, -10.4337, 3.3446, 66.1166, -10.4337, 3.3446, 66.1166, -10.4337, 0, 75.8157, -10.4337, 6.6893, 72.0437, -10.4337, 6.6893, 72.0614, -10.4337, 3.3446, 72.0437, -10.4337, 6.6893, 68.9463, -10.4337, 3.3446, 72.0614, -10.4337, 3.3446, 79.236, -10.4337, 3.3446, 75.8157, -10.4337, 6.6893, 75.804695, -10.4337, 3.3446, 75.8157, -10.4337, 6.6893, 72.0614, -10.4337, 3.3446, 75.804695, -10.4337, 3.3446, 79.236, -10.4337, 0, 79.236, -10.4337, 3.3446, 75.804695, -10.4337, 3.3446, 68.9463, -10.4337, 3.3446, 66.1166, -10.4337, 0, 68.9478, -10.4337, 0, 72.0614, -10.4337, 3.3446, 68.9463, -10.4337, 3.3446, 68.9478, -10.4337, 0, 68.9478, -10.4337, 0, 66.1166, -10.4337, 0, 66.1166, -10.4337, -3.3446002, 75.804695, -10.4337, 3.3446, 72.0614, -10.4337, 3.3446, 72.078995, -10.4337, 0, 72.0614, -10.4337, 3.3446, 68.9478, -10.4337, 0, 72.078995, -10.4337, 0, 79.236, -10.4337, 0, 75.804695, -10.4337, 3.3446, 75.793594, -10.4337, 0, 75.804695, -10.4337, 3.3446, 72.078995, -10.4337, 0, 75.793594, -10.4337, 0, 79.236, -10.4337, -3.3446002, 79.236, -10.4337, 0, 75.793594, -10.4337, 0, 68.9478, -10.4337, 0, 66.1166, -10.4337, -3.3446002, 68.9494, -10.4337, -3.3446002, 72.078995, -10.4337, 0, 68.9478, -10.4337, 0, 68.9494, -10.4337, -3.3446002, 68.9494, -10.4337, -3.3446002, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, -6.6893, 75.793594, -10.4337, 0, 72.078995, -10.4337, 0, 72.096695, -10.4337, -3.3446002, 72.078995, -10.4337, 0, 68.9494, -10.4337, -3.3446002, 72.096695, -10.4337, -3.3446002, 79.236, -10.4337, -3.3446002, 75.793594, -10.4337, 0, 75.7826, -10.4337, -3.3446002, 75.793594, -10.4337, 0, 72.096695, -10.4337, -3.3446002, 75.7826, -10.4337, -3.3446002, 79.236, -10.4337, -6.6893, 79.236, -10.4337, -3.3446002, 75.7826, -10.4337, -3.3446002, 68.9494, -10.4337, -3.3446002, 66.1166, -10.4337, -6.6893, 68.9509, -10.4337, -6.6893, 72.096695, -10.4337, -3.3446002, 68.9494, -10.4337, -3.3446002, 68.9509, -10.4337, -6.6893, 68.9509, -10.4337, -6.6893, 66.1166, -10.4337, -6.6893, 66.1166, -10.4337, -10.033899, 75.7826, -10.4337, -3.3446002, 72.096695, -10.4337, -3.3446002, 72.114296, -10.4337, -6.6893, 72.096695, -10.4337, -3.3446002, 68.9509, -10.4337, -6.6893, 72.114296, -10.4337, -6.6893, 79.236, -10.4337, -6.6893, 75.7826, -10.4337, -3.3446002, 75.7716, -10.4337, -6.6893, 75.7826, -10.4337, -3.3446002, 72.114296, -10.4337, -6.6893, 75.7716, -10.4337, -6.6893, 79.236, -10.4337, -10.033899, 79.236, -10.4337, -6.6893, 75.7716, -10.4337, -6.6893, 68.9509, -10.4337, -6.6893, 66.1166, -10.4337, -10.033899, 68.9524, -10.4337, -10.033899, 72.114296, -10.4337, -6.6893, 68.9509, -10.4337, -6.6893, 68.9524, -10.4337, -10.033899, 68.9524, -10.4337, -10.033899, 66.1166, -10.4337, -10.033899, 66.1166, -10.4337, -13.378599, 75.7716, -10.4337, -6.6893, 72.114296, -10.4337, -6.6893, 72.1319, -10.4337, -10.033899, 72.114296, -10.4337, -6.6893, 68.9524, -10.4337, -10.033899, 72.1319, -10.4337, -10.033899, 79.236, -10.4337, -10.033899, 75.7716, -10.4337, -6.6893, 75.7606, -10.4337, -10.033899, 75.7716, -10.4337, -6.6893, 72.1319, -10.4337, -10.033899, 75.7606, -10.4337, -10.033899, 79.236, -10.4337, -13.378599, 79.236, -10.4337, -10.033899, 75.7606, -10.4337, -10.033899, 68.9524, -10.4337, -10.033899, 66.1166, -10.4337, -13.378599, 68.953995, -10.4337, -13.378599, 72.1319, -10.4337, -10.033899, 68.9524, -10.4337, -10.033899, 68.953995, -10.4337, -13.378599, 68.953995, -10.4337, -13.378599, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -16.7232, 75.7606, -10.4337, -10.033899, 72.1319, -10.4337, -10.033899, 72.1496, -10.4337, -13.378599, 72.1319, -10.4337, -10.033899, 68.953995, -10.4337, -13.378599, 72.1496, -10.4337, -13.378599, 79.236, -10.4337, -13.378599, 75.7606, -10.4337, -10.033899, 75.749596, -10.4337, -13.378599, 75.7606, -10.4337, -10.033899, 72.1496, -10.4337, -13.378599, 75.749596, -10.4337, -13.378599, 79.236, -10.4337, -16.7232, 79.236, -10.4337, -13.378599, 75.749596, -10.4337, -13.378599, 68.953995, -10.4337, -13.378599, 66.1166, -10.4337, -16.7232, 68.9555, -10.4337, -16.7232, 72.1496, -10.4337, -13.378599, 68.953995, -10.4337, -13.378599, 68.9555, -10.4337, -16.7232, 68.9555, -10.4337, -16.7232, 66.1166, -10.4337, -16.7232, 66.1166, -10.4337, -20.0679, 75.749596, -10.4337, -13.378599, 72.1496, -10.4337, -13.378599, 72.1672, -10.4337, -16.7232, 72.1496, -10.4337, -13.378599, 68.9555, -10.4337, -16.7232, 72.1672, -10.4337, -16.7232, 79.236, -10.4337, -16.7232, 75.749596, -10.4337, -13.378599, 75.738495, -10.4337, -16.7232, 75.749596, -10.4337, -13.378599, 72.1672, -10.4337, -16.7232, 75.738495, -10.4337, -16.7232, 79.236, -10.4337, -20.0679, 79.236, -10.4337, -16.7232, 75.738495, -10.4337, -16.7232, 68.9555, -10.4337, -16.7232, 66.1166, -10.4337, -20.0679, 68.9571, -10.4337, -20.0679, 72.1672, -10.4337, -16.7232, 68.9555, -10.4337, -16.7232, 68.9571, -10.4337, -20.0679, 68.9571, -10.4337, -20.0679, 66.1166, -10.4337, -20.0679, 66.1166, -10.4337, -23.412498, 68.9571, -10.4337, -20.0679, 66.1166, -10.4337, -23.412498, 68.958595, -10.4337, -23.412498, 72.1672, -10.4337, -16.7232, 68.9571, -10.4337, -20.0679, 72.1849, -10.4337, -20.0679, 72.1849, -10.4337, -20.0679, 68.9571, -10.4337, -20.0679, 68.958595, -10.4337, -23.412498, 75.738495, -10.4337, -16.7232, 72.1672, -10.4337, -16.7232, 72.1849, -10.4337, -20.0679, 72.1849, -10.4337, -20.0679, 68.958595, -10.4337, -23.412498, 72.2025, -10.4337, -23.412498, 79.236, -10.4337, -20.0679, 75.738495, -10.4337, -16.7232, 75.7275, -10.4337, -20.0679, 75.738495, -10.4337, -16.7232, 72.1849, -10.4337, -20.0679, 75.7275, -10.4337, -20.0679, 75.7275, -10.4337, -20.0679, 72.1849, -10.4337, -20.0679, 72.2025, -10.4337, -23.412498, 79.236, -10.4337, -23.412498, 79.236, -10.4337, -20.0679, 75.7275, -10.4337, -20.0679, 75.7275, -10.4337, -20.0679, 72.2025, -10.4337, -23.412498, 75.7165, -10.4337, -23.412498, 79.236, -10.4337, -23.412498, 75.7275, -10.4337, -20.0679, 75.7165, -10.4337, -23.412498, -38.5239, -3.6573, 10.033899, -41.627, -4.8403997, 6.6893, -38.5239, -3.6573, 6.6893, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 13.378601, -41.627, -4.8403997, 6.6893, -41.627, -4.8403997, 3.3446, -38.5239, -3.6573, 6.6893, -41.627, -4.8403997, 6.6893, -41.627, -4.8403997, 3.3446, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 6.6893, -41.627, -4.8403997, 6.6893, -38.5239, -3.6573, 13.378601, -41.627, -4.8403997, 13.378601, -41.627, -4.8403997, 0, -38.5239, -3.6573, 3.3446, -41.627, -4.8403997, 3.3446, -41.627, -4.8403997, 0, -38.5239, -3.6573, 0, -38.5239, -3.6573, 3.3446, -44.537098, -5.95, 3.3446, -41.627, -4.8403997, 3.3446, -41.627, -4.8403997, 6.6893, -41.627, -4.8403997, -3.3446002, -38.5239, -3.6573, 0, -41.627, -4.8403997, 0, -41.627, -4.8403997, -3.3446002, -38.5239, -3.6573, -3.3446002, -38.5239, -3.6573, 0, -44.537098, -5.95, 0, -41.627, -4.8403997, 0, -41.627, -4.8403997, 3.3446, -44.537098, -5.95, 0, -41.627, -4.8403997, 3.3446, -44.537098, -5.95, 3.3446, -44.537098, -5.95, -3.3446002, -41.627, -4.8403997, -3.3446002, -41.627, -4.8403997, 0, -44.537098, -5.95, -3.3446002, -41.627, -4.8403997, 0, -44.537098, -5.95, 0, -41.627, -4.8403997, -6.6893, -38.5239, -3.6573, -3.3446002, -41.627, -4.8403997, -3.3446002, -41.627, -4.8403997, -6.6893, -38.5239, -3.6573, -6.6893, -38.5239, -3.6573, -3.3446002, -44.537098, -5.95, -6.6893, -41.627, -4.8403997, -3.3446002, -44.537098, -5.95, -3.3446002, -44.537098, -5.95, -6.6893, -41.627, -4.8403997, -6.6893, -41.627, -4.8403997, -3.3446002, -41.627, -4.8403997, -10.033899, -38.5239, -3.6573, -6.6893, -41.627, -4.8403997, -6.6893, -41.627, -4.8403997, -10.033899, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -6.6893, -44.537098, -5.95, -10.033899, -41.627, -4.8403997, -6.6893, -44.537098, -5.95, -6.6893, -44.537098, -5.95, -10.033899, -41.627, -4.8403997, -10.033899, -41.627, -4.8403997, -6.6893, -41.627, -4.8403997, -13.378599, -38.5239, -3.6573, -10.033899, -41.627, -4.8403997, -10.033899, -41.627, -4.8403997, -13.378599, -38.5239, -3.6573, -13.378599, -38.5239, -3.6573, -10.033899, -41.627, -4.8403997, -13.378599, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -13.378599, -41.627, -4.8403997, -13.378599, -38.5239, -3.6573, -17.0441, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -17.0441, -41.627, -4.8403997, -13.378599, -41.627, -4.8403997, -17.0159, -41.627, -4.8403997, -17.0159, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -17.0441, -44.537098, -5.95, -13.378599, -41.627, -4.8403997, -13.378599, -41.627, -4.8403997, -10.033899, -44.537098, -5.95, -13.378599, -41.627, -4.8403997, -10.033899, -44.537098, -5.95, -10.033899, -41.627, -4.8403997, -17.0159, -38.5239, -3.6573, -20.3888, -38.5239, -3.6573, -20.0679, -44.537098, -5.95, -16.9895, -41.627, -4.8403997, -13.378599, -44.537098, -5.95, -13.378599, -44.537098, -5.95, -16.9895, -41.627, -4.8403997, -17.0159, -41.627, -4.8403997, -13.378599, -47.4473, -7.0596, -13.378599, -44.537098, -5.95, -13.378599, -44.537098, -5.95, -10.033899, -47.4473, -7.0596, -16.963, -44.537098, -5.95, -16.9895, -44.537098, -5.95, -13.378599, -47.4473, -7.0596, -16.963, -44.537098, -5.95, -13.378599, -47.4473, -7.0596, -13.378599, -44.537098, -5.95, -20.334099, -41.627, -4.8403997, -17.0159, -44.537098, -5.95, -16.9895, -38.5239, -3.6573, -20.3888, -41.627, -4.8403997, -17.0159, -41.627, -4.8403997, -20.3606, -44.537098, -5.95, -20.334099, -41.627, -4.8403997, -20.3606, -41.627, -4.8403997, -17.0159, -41.627, -4.8403997, -23.412498, -38.5239, -3.6573, -20.3888, -41.627, -4.8403997, -20.3606, -41.627, -4.8403997, -23.412498, -38.5239, -3.6573, -23.412498, -38.5239, -3.6573, -20.3888, -41.627, -4.8403997, -26.7572, -38.5239, -3.6573, -23.412498, -41.627, -4.8403997, -23.412498, -41.627, -4.8403997, -26.7572, -38.5239, -3.6573, -26.7572, -38.5239, -3.6573, -23.412498, -44.537098, -5.95, -26.7572, -41.627, -4.8403997, -26.7572, -41.627, -4.8403997, -23.412498, -44.537098, -5.95, -23.412498, -41.627, -4.8403997, -23.412498, -41.627, -4.8403997, -20.3606, -44.537098, -5.95, -26.7572, -41.627, -4.8403997, -23.412498, -44.537098, -5.95, -23.412498, -44.537098, -5.95, -23.412498, -41.627, -4.8403997, -20.3606, -44.537098, -5.95, -20.334099, -47.4473, -7.0596, -26.7572, -44.537098, -5.95, -26.7572, -44.537098, -5.95, -23.412498, -47.4473, -7.0596, -23.412498, -44.537098, -5.95, -23.412498, -44.537098, -5.95, -20.334099, -47.4473, -7.0596, -26.7572, -44.537098, -5.95, -23.412498, -47.4473, -7.0596, -23.412498, -50.290497, -8.1437, -23.412498, -47.4473, -7.0596, -26.7572, -47.4473, -7.0596, -23.412498, -50.290497, -8.1437, -23.412498, -50.290497, -8.1437, -26.7572, -47.4473, -7.0596, -26.7572, -50.290497, -8.1437, -20.2818, -50.290497, -8.1437, -23.412498, -47.4473, -7.0596, -23.412498, -47.4473, -7.0596, -23.412498, -44.537098, -5.95, -20.334099, -47.4473, -7.0596, -20.3077, -50.290497, -8.1437, -20.2818, -47.4473, -7.0596, -23.412498, -47.4473, -7.0596, -20.3077, -47.4473, -7.0596, -20.3077, -44.537098, -5.95, -20.334099, -44.537098, -5.95, -16.9895, -47.4473, -7.0596, -20.3077, -44.537098, -5.95, -16.9895, -47.4473, -7.0596, -16.963, -47.4473, -7.0596, -16.963, -50.290497, -8.1437, -20.2818, -47.4473, -7.0596, -20.3077, -47.4473, -7.0596, -16.963, -50.290497, -8.1437, -20.0679, -50.290497, -8.1437, -20.2818, -50.290497, -8.1437, -20.0679, -47.4473, -7.0596, -16.963, -50.290497, -8.1437, -16.937199, -47.4473, -7.0596, -13.378599, -50.290497, -8.1437, -16.937199, -47.4473, -7.0596, -16.963, -47.4473, -7.0596, -13.378599, -50.290497, -8.1437, -16.7232, -50.290497, -8.1437, -16.937199, -50.290497, -8.1437, -16.7232, -47.4473, -7.0596, -13.378599, -50.290497, -8.1437, -13.378599, -50.290497, -8.1437, -10.033899, -50.290497, -8.1437, -13.378599, -47.4473, -7.0596, -13.378599, -50.290497, -8.1437, -10.033899, -47.4473, -7.0596, -13.378599, -47.4473, -7.0596, -10.033899, -47.4473, -7.0596, -13.378599, -44.537098, -5.95, -10.033899, -47.4473, -7.0596, -10.033899, -50.290497, -8.1437, -6.6893, -50.290497, -8.1437, -10.033899, -47.4473, -7.0596, -10.033899, -47.4473, -7.0596, -10.033899, -44.537098, -5.95, -10.033899, -44.537098, -5.95, -6.6893, -50.290497, -8.1437, -6.6893, -47.4473, -7.0596, -10.033899, -47.4473, -7.0596, -6.6893, -47.4473, -7.0596, -10.033899, -44.537098, -5.95, -6.6893, -47.4473, -7.0596, -6.6893, -50.290497, -8.1437, -3.3446002, -50.290497, -8.1437, -6.6893, -47.4473, -7.0596, -6.6893, -47.4473, -7.0596, -6.6893, -44.537098, -5.95, -6.6893, -44.537098, -5.95, -3.3446002, -50.290497, -8.1437, -3.3446002, -47.4473, -7.0596, -6.6893, -47.4473, -7.0596, -3.3446002, -47.4473, -7.0596, -6.6893, -44.537098, -5.95, -3.3446002, -47.4473, -7.0596, -3.3446002, -50.290497, -8.1437, 0, -50.290497, -8.1437, -3.3446002, -47.4473, -7.0596, -3.3446002, -47.4473, -7.0596, -3.3446002, -44.537098, -5.95, -3.3446002, -44.537098, -5.95, 0, -50.290497, -8.1437, 0, -47.4473, -7.0596, -3.3446002, -47.4473, -7.0596, 0, -47.4473, -7.0596, -3.3446002, -44.537098, -5.95, 0, -47.4473, -7.0596, 0, -50.290497, -8.1437, 3.3446, -50.290497, -8.1437, 0, -47.4473, -7.0596, 0, -47.4473, -7.0596, 0, -44.537098, -5.95, 0, -44.537098, -5.95, 3.3446, -50.290497, -8.1437, 3.3446, -47.4473, -7.0596, 0, -47.4473, -7.0596, 3.3446, -47.4473, -7.0596, 0, -44.537098, -5.95, 3.3446, -47.4473, -7.0596, 3.3446, -50.290497, -8.1437, 6.6893, -50.290497, -8.1437, 3.3446, -47.4473, -7.0596, 3.3446, -50.290497, -8.1437, 6.6893, -47.4473, -7.0596, 3.3446, -47.4473, -7.0596, 6.6893, -47.4473, -7.0596, 6.6893, -50.290497, -8.1437, 10.033899, -50.290497, -8.1437, 6.6893, -47.4473, -7.0596, 3.3446, -44.537098, -5.95, 3.3446, -44.537098, -5.95, 6.6893, -47.4473, -7.0596, 3.3446, -44.537098, -5.95, 6.6893, -47.4473, -7.0596, 6.6893, -44.537098, -5.95, 3.3446, -41.627, -4.8403997, 6.6893, -44.537098, -5.95, 6.6893, -47.4473, -7.0596, 6.6893, -50.290497, -8.1437, 13.378601, -50.290497, -8.1437, 10.033899, -41.627, -4.8403997, 13.378601, -44.537098, -5.95, 6.6893, -41.627, -4.8403997, 6.6893, -44.537098, -5.95, 13.378601, -47.4473, -7.0596, 6.6893, -44.537098, -5.95, 6.6893, -41.627, -4.8403997, 13.378601, -44.537098, -5.95, 13.378601, -44.537098, -5.95, 6.6893, -44.537098, -5.95, 13.378601, -47.4473, -7.0596, 13.378601, -47.4473, -7.0596, 6.6893, -50.290497, -8.1437, 13.378601, -47.4473, -7.0596, 6.6893, -47.4473, -7.0596, 13.378601, -41.627, -4.8403997, 16.7232, -44.537098, -5.95, 13.378601, -41.627, -4.8403997, 13.378601, -38.5239, -3.6573, 13.378601, -41.627, -4.8403997, 16.7232, -41.627, -4.8403997, 13.378601, -38.5239, -3.6573, 13.378601, -38.5239, -3.6573, 16.7232, -41.627, -4.8403997, 16.7232, -41.627, -4.8403997, 16.7232, -44.537098, -5.95, 16.7232, -44.537098, -5.95, 13.378601, -44.537098, -5.95, 16.7232, -47.4473, -7.0596, 13.378601, -44.537098, -5.95, 13.378601, -38.5239, -3.6573, 16.7232, -41.627, -4.8403997, 20.0679, -41.627, -4.8403997, 16.7232, -41.627, -4.8403997, 20.0679, -44.537098, -5.95, 16.7232, -41.627, -4.8403997, 16.7232, -38.5239, -3.6573, 16.7232, -38.5239, -3.6573, 20.0679, -41.627, -4.8403997, 20.0679, -47.4473, -7.0596, 16.7232, -50.290497, -8.1437, 13.378601, -47.4473, -7.0596, 13.378601, -44.537098, -5.95, 16.7232, -47.4473, -7.0596, 16.7232, -47.4473, -7.0596, 13.378601, -47.4473, -7.0596, 16.7232, -50.290497, -8.1437, 16.7232, -50.290497, -8.1437, 13.378601, -41.627, -4.8403997, 20.0679, -44.537098, -5.95, 20.0679, -44.537098, -5.95, 16.7232, -44.537098, -5.95, 20.0679, -47.4473, -7.0596, 16.7232, -44.537098, -5.95, 16.7232, -38.5239, -3.6573, 20.0679, -41.627, -4.8403997, 23.412498, -41.627, -4.8403997, 20.0679, -41.627, -4.8403997, 23.412498, -44.537098, -5.95, 20.0679, -41.627, -4.8403997, 20.0679, -38.5239, -3.6573, 20.0679, -38.5239, -3.6573, 23.412498, -41.627, -4.8403997, 23.412498, -38.5239, -3.6573, 23.412498, -41.627, -4.8403997, 26.7572, -41.627, -4.8403997, 23.412498, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 26.7572, -41.627, -4.8403997, 26.7572, -41.627, -4.8403997, 26.7572, -44.537098, -5.95, 23.412498, -41.627, -4.8403997, 23.412498, -41.627, -4.8403997, 23.412498, -44.537098, -5.95, 23.412498, -44.537098, -5.95, 20.0679, -41.627, -4.8403997, 26.7572, -44.537098, -5.95, 26.7572, -44.537098, -5.95, 23.412498, -44.537098, -5.95, 20.0679, -47.4473, -7.0596, 20.0679, -47.4473, -7.0596, 16.7232, -47.4473, -7.0596, 20.0679, -50.290497, -8.1437, 16.7232, -47.4473, -7.0596, 16.7232, -44.537098, -5.95, 23.412498, -47.4473, -7.0596, 20.0679, -44.537098, -5.95, 20.0679, -47.4473, -7.0596, 20.0679, -50.290497, -8.1437, 20.0679, -50.290497, -8.1437, 16.7232, -44.537098, -5.95, 26.7572, -47.4473, -7.0596, 23.412498, -44.537098, -5.95, 23.412498, -44.537098, -5.95, 23.412498, -47.4473, -7.0596, 23.412498, -47.4473, -7.0596, 20.0679, -47.4473, -7.0596, 23.412498, -50.290497, -8.1437, 20.0679, -47.4473, -7.0596, 20.0679, -44.537098, -5.95, 26.7572, -47.4473, -7.0596, 26.7572, -47.4473, -7.0596, 23.412498, -47.4473, -7.0596, 23.412498, -50.290497, -8.1437, 23.412498, -50.290497, -8.1437, 20.0679, -47.4473, -7.0596, 26.7572, -50.290497, -8.1437, 23.412498, -47.4473, -7.0596, 23.412498, -47.4473, -7.0596, 26.7572, -50.290497, -8.1437, 26.7572, -50.290497, -8.1437, 23.412498, -6.6893, -8.1437, -54.500298, -3.4038, -8.6382, -57.495697, -3.4199996, -8.1437, -54.500298, -3.4038, -8.6382, -57.495697, -3.3446002, -8.1437, -54.500298, -3.4199996, -8.1437, -54.500298, -6.6893, -8.1437, -54.500298, -6.7919, -8.1437, -54.500298, -3.4038, -8.6382, -57.495697, -3.4038, -8.6382, -57.495697, -0.048199892, -8.1437, -54.500298, -3.3446002, -8.1437, -54.500298, -3.4038, -8.6382, -57.495697, -6.7919, -8.1437, -54.500298, -6.7697997, -8.6367, -57.4865, -10.033899, -8.1437, -54.500298, -6.7697997, -8.6367, -57.4865, -6.7919, -8.1437, -54.500298, -0.048199892, -8.1437, -54.500298, -3.4038, -8.6382, -57.495697, -0.037699938, -8.6397, -57.504997, -6.7697997, -8.6367, -57.4865, -10.033899, -8.1437, -54.500298, -10.141901, -8.1437, -54.500298, -0.037699938, -8.6397, -57.504997, 0, -8.1437, -54.500298, -0.048199892, -8.1437, -54.500298, -6.7697997, -8.6367, -57.4865, -10.141901, -8.1437, -54.500298, -10.1187, -8.635099, -57.4772, -13.378601, -8.1437, -54.500298, -10.1187, -8.635099, -57.4772, -10.141901, -8.1437, -54.500298, -0.037699938, -8.6397, -57.504997, 3.3446, -8.1437, -54.500298, 0, -8.1437, -54.500298, -10.1187, -8.635099, -57.4772, -13.378601, -8.1437, -54.500298, -13.4286, -8.1437, -54.500298, 3.3446, -8.1437, -54.500298, -0.037699938, -8.6397, -57.504997, 3.3446, -8.6413, -57.514297, -10.1187, -8.635099, -57.4772, -6.749, -9.1004, -60.295498, -6.7697997, -8.6367, -57.4865, 6.6893, -8.642799, -57.5235, 3.3446, -8.1437, -54.500298, 3.3446, -8.6413, -57.514297, 6.6893, -8.642799, -57.5235, 6.6893, -8.1437, -54.500298, 3.3446, -8.1437, -54.500298, 3.3446, -9.0949, -60.262497, 3.3446, -8.6413, -57.514297, -0.037699938, -8.6397, -57.504997, 10.033899, -8.6442995, -57.5327, 6.6893, -8.1437, -54.500298, 6.6893, -8.642799, -57.5235, 10.033899, -8.6442995, -57.5327, 10.033899, -8.1437, -54.500298, 6.6893, -8.1437, -54.500298, 10.033899, -8.6442995, -57.5327, 13.378599, -8.1437, -54.500298, 10.033899, -8.1437, -54.500298, 10.033899, -8.6442995, -57.5327, 13.444599, -8.1437, -54.500298, 13.378599, -8.1437, -54.500298, 13.444599, -8.1437, -54.500298, 10.033899, -8.6442995, -57.5327, 13.4300995, -8.6458, -57.5421, 13.4300995, -8.6458, -57.5421, 16.7232, -8.1437, -54.500298, 13.444599, -8.1437, -54.500298, 13.4300995, -8.6458, -57.5421, 16.8188, -8.1437, -54.500298, 16.7232, -8.1437, -54.500298, 16.8188, -8.1437, -54.500298, 13.4300995, -8.6458, -57.5421, 16.7978, -8.6474, -57.5514, 16.7978, -8.6474, -57.5514, 20.0679, -8.1437, -54.500298, 16.8188, -8.1437, -54.500298, 16.7978, -8.6474, -57.5514, 20.1975, -8.1437, -54.500298, 20.0679, -8.1437, -54.500298, 20.1975, -8.1437, -54.500298, 16.7978, -8.6474, -57.5514, 20.1689, -8.6489, -57.5607, 20.1442, -9.085899, -60.207497, 20.1689, -8.6489, -57.5607, 16.7978, -8.6474, -57.5514, 20.1442, -9.085899, -60.207497, 16.7978, -8.6474, -57.5514, 16.7794, -9.0877, -60.2185, 16.7794, -9.0877, -60.2185, 16.7978, -8.6474, -57.5514, 13.4300995, -8.6458, -57.5421, 20.112099, -9.6529, -63.642, 20.1442, -9.085899, -60.207497, 16.7794, -9.0877, -60.2185, 16.7794, -9.0877, -60.2185, 13.4300995, -8.6458, -57.5421, 13.417299, -9.089499, -60.2295, 13.417299, -9.089499, -60.2295, 13.4300995, -8.6458, -57.5421, 10.033899, -8.6442995, -57.5327, 20.112099, -9.6529, -63.642, 16.7794, -9.0877, -60.2185, 16.7557, -9.6571, -63.6679, 16.7557, -9.6571, -63.6679, 16.7794, -9.0877, -60.2185, 13.417299, -9.089499, -60.2295, 16.7232, -10.4337, -68.371895, 20.112099, -9.6529, -63.642, 16.7557, -9.6571, -63.6679, 16.7232, -10.4337, -68.371895, 20.0679, -10.4337, -68.371895, 20.112099, -9.6529, -63.642, 13.378599, -10.4337, -68.371895, 16.7232, -10.4337, -68.371895, 16.7557, -9.6571, -63.6679, 13.378599, -10.4337, -68.371895, 16.7557, -9.6571, -63.6679, 13.400799, -9.6614, -63.693794, 16.7557, -9.6571, -63.6679, 13.417299, -9.089499, -60.2295, 13.400799, -9.6614, -63.693794, 10.033899, -10.4337, -68.371895, 13.378599, -10.4337, -68.371895, 13.400799, -9.6614, -63.693794, 13.417299, -9.089499, -60.2295, 10.033899, -8.6442995, -57.5327, 10.033899, -9.0913, -60.240597, 13.400799, -9.6614, -63.693794, 13.417299, -9.089499, -60.2295, 10.033899, -9.0913, -60.240597, 10.033899, -9.0913, -60.240597, 10.033899, -8.6442995, -57.5327, 6.6893, -8.642799, -57.5235, 10.033899, -10.4337, -68.371895, 13.400799, -9.6614, -63.693794, 10.033899, -9.6657, -63.719696, 13.400799, -9.6614, -63.693794, 10.033899, -9.0913, -60.240597, 10.033899, -9.6657, -63.719696, 6.6893, -10.4337, -68.371895, 10.033899, -10.4337, -68.371895, 10.033899, -9.6657, -63.719696, 10.033899, -9.0913, -60.240597, 6.6893, -8.642799, -57.5235, 6.6893, -9.0931, -60.2515, 10.033899, -9.6657, -63.719696, 10.033899, -9.0913, -60.240597, 6.6893, -9.0931, -60.2515, 6.6893, -9.0931, -60.2515, 6.6893, -8.642799, -57.5235, 3.3446, -8.6413, -57.514297, 6.6893, -10.4337, -68.371895, 10.033899, -9.6657, -63.719696, 6.6893, -9.6699, -63.7455, 10.033899, -9.6657, -63.719696, 6.6893, -9.0931, -60.2515, 6.6893, -9.6699, -63.7455, 3.3446, -10.4337, -68.371895, 6.6893, -10.4337, -68.371895, 6.6893, -9.6699, -63.7455, 6.6893, -9.0931, -60.2515, 3.3446, -8.6413, -57.514297, 3.3446, -9.0949, -60.262497, 6.6893, -9.6699, -63.7455, 6.6893, -9.0931, -60.2515, 3.3446, -9.0949, -60.262497, 3.3446, -10.4337, -68.371895, 6.6893, -9.6699, -63.7455, 3.3446, -9.6742, -63.771194, 6.6893, -9.6699, -63.7455, 3.3446, -9.0949, -60.262497, 3.3446, -9.6742, -63.771194, 0, -10.4337, -68.371895, 3.3446, -10.4337, -68.371895, 3.3446, -9.6742, -63.771194, 3.3446, -9.6742, -63.771194, 3.3446, -9.0949, -60.262497, -0.028100014, -9.0968, -60.2735, 3.3446, -9.0949, -60.262497, -0.037699938, -8.6397, -57.504997, -0.028100014, -9.0968, -60.2735, -0.028100014, -9.0968, -60.2735, -0.037699938, -8.6397, -57.504997, -3.4038, -8.6382, -57.495697, 0, -10.4337, -68.371895, 3.3446, -9.6742, -63.771194, -0.015899897, -9.6785, -63.797096, 3.3446, -9.6742, -63.771194, -0.028100014, -9.0968, -60.2735, -0.015899897, -9.6785, -63.797096, -3.3446002, -10.4337, -68.371895, 0, -10.4337, -68.371895, -0.015899897, -9.6785, -63.797096, -0.028100014, -9.0968, -60.2735, -3.4038, -8.6382, -57.495697, -3.3885999, -9.098599, -60.2845, -0.015899897, -9.6785, -63.797096, -0.028100014, -9.0968, -60.2735, -3.3885999, -9.098599, -60.2845, -6.7697997, -8.6367, -57.4865, -3.3885999, -9.098599, -60.2845, -3.4038, -8.6382, -57.495697, -6.7697997, -8.6367, -57.4865, -6.749, -9.1004, -60.295498, -3.3885999, -9.098599, -60.2845, -0.015899897, -9.6785, -63.797096, -3.3885999, -9.098599, -60.2845, -3.3694, -9.6827, -63.8229, -3.3446002, -10.4337, -68.371895, -0.015899897, -9.6785, -63.797096, -3.3694, -9.6827, -63.8229, -6.749, -9.1004, -60.295498, -3.3694, -9.6827, -63.8229, -3.3885999, -9.098599, -60.2845, -6.7226996, -9.686999, -63.848793, -3.3446002, -10.4337, -68.371895, -3.3694, -9.6827, -63.8229, -6.749, -9.1004, -60.295498, -6.7226996, -9.686999, -63.848793, -3.3694, -9.6827, -63.8229, -6.7226996, -9.686999, -63.848793, -6.6893, -10.4337, -68.371895, -3.3446002, -10.4337, -68.371895, -10.096701, -9.1022, -60.3065, -6.7226996, -9.686999, -63.848793, -6.749, -9.1004, -60.295498, -10.068901, -9.691199, -63.874596, -6.6893, -10.4337, -68.371895, -6.7226996, -9.686999, -63.848793, -10.096701, -9.1022, -60.3065, -10.068901, -9.691199, -63.874596, -6.7226996, -9.686999, -63.848793, -10.068901, -9.691199, -63.874596, -10.033899, -10.4337, -68.371895, -6.6893, -10.4337, -68.371895, -10.1187, -8.635099, -57.4772, -10.096701, -9.1022, -60.3065, -6.749, -9.1004, -60.295498, -13.394701, -9.695499, -63.9002, -10.033899, -10.4337, -68.371895, -10.068901, -9.691199, -63.874596, -13.394701, -9.695499, -63.9002, -13.378601, -10.4337, -68.371895, -10.033899, -10.4337, -68.371895, -13.4076, -9.104, -60.3173, -10.068901, -9.691199, -63.874596, -10.096701, -9.1022, -60.3065, -13.4076, -9.104, -60.3173, -13.394701, -9.695499, -63.9002, -10.068901, -9.691199, -63.874596, -13.4179, -8.6336, -57.468098, -10.096701, -9.1022, -60.3065, -10.1187, -8.635099, -57.4772, -13.4179, -8.6336, -57.468098, -13.4076, -9.104, -60.3173, -10.096701, -9.1022, -60.3065, -10.1187, -8.635099, -57.4772, -13.4286, -8.1437, -54.500298, -13.4179, -8.6336, -57.468098, -13.4286, -8.1437, -54.500298, -16.7232, -8.6321, -57.459, -13.4179, -8.6336, -57.468098, -16.7232, -8.6321, -57.459, -13.4076, -9.104, -60.3173, -13.4179, -8.6336, -57.468098, -13.4286, -8.1437, -54.500298, -16.7232, -8.1437, -54.500298, -16.7232, -8.6321, -57.459, -16.7232, -9.1058, -60.3281, -13.394701, -9.695499, -63.9002, -13.4076, -9.104, -60.3173, -16.7232, -8.6321, -57.459, -16.7232, -9.1058, -60.3281, -13.4076, -9.104, -60.3173, -16.7232, -9.699699, -63.925797, -13.378601, -10.4337, -68.371895, -13.394701, -9.695499, -63.9002, -16.7232, -9.1058, -60.3281, -16.7232, -9.699699, -63.925797, -13.394701, -9.695499, -63.9002, -16.7232, -9.699699, -63.925797, -16.7232, -10.4337, -68.371895, -13.378601, -10.4337, -68.371895, -16.7232, -8.1437, -54.500298, -20.0679, -8.6306, -57.4498, -16.7232, -8.6321, -57.459, -20.0679, -8.6306, -57.4498, -16.7232, -9.1058, -60.3281, -16.7232, -8.6321, -57.459, -16.7232, -8.1437, -54.500298, -20.0679, -8.1437, -54.500298, -20.0679, -8.6306, -57.4498, -20.0679, -9.1076, -60.3391, -16.7232, -9.699699, -63.925797, -16.7232, -9.1058, -60.3281, -20.0679, -8.6306, -57.4498, -20.0679, -9.1076, -60.3391, -16.7232, -9.1058, -60.3281, -20.0679, -9.7039995, -63.9516, -16.7232, -10.4337, -68.371895, -16.7232, -9.699699, -63.925797, -20.0679, -9.1076, -60.3391, -20.0679, -9.7039995, -63.9516, -16.7232, -9.699699, -63.925797, -20.0679, -9.7039995, -63.9516, -20.0679, -10.4337, -68.371895, -16.7232, -10.4337, -68.371895, -20.0679, -8.1437, -54.500298, -23.412498, -8.6291, -57.440598, -20.0679, -8.6306, -57.4498, -23.412498, -8.6291, -57.440598, -20.0679, -9.1076, -60.3391, -20.0679, -8.6306, -57.4498, -20.0679, -8.1437, -54.500298, -23.412498, -8.1437, -54.500298, -23.412498, -8.6291, -57.440598, -23.412498, -8.1437, -54.500298, -26.7572, -8.6276, -57.4314, -23.412498, -8.6291, -57.440598, -23.412498, -8.1437, -54.500298, -26.7572, -8.1437, -54.500298, -26.7572, -8.6276, -57.4314, -23.412498, -8.6291, -57.440598, -23.412498, -9.1094, -60.35, -20.0679, -9.1076, -60.3391, -26.7572, -8.6276, -57.4314, -23.412498, -9.1094, -60.35, -23.412498, -8.6291, -57.440598, -23.412498, -9.1094, -60.35, -20.0679, -9.7039995, -63.9516, -20.0679, -9.1076, -60.3391, -26.7572, -8.6276, -57.4314, -26.7572, -9.111199, -60.361, -23.412498, -9.1094, -60.35, -23.412498, -9.7081995, -63.977394, -20.0679, -10.4337, -68.371895, -20.0679, -9.7039995, -63.9516, -23.412498, -9.1094, -60.35, -23.412498, -9.7081995, -63.977394, -20.0679, -9.7039995, -63.9516, -26.7572, -9.111199, -60.361, -23.412498, -9.7081995, -63.977394, -23.412498, -9.1094, -60.35, -23.412498, -9.7081995, -63.977394, -23.412498, -10.4337, -68.371895, -20.0679, -10.4337, -68.371895, -26.7572, -9.111199, -60.361, -26.7572, -9.7125, -64.0031, -23.412498, -9.7081995, -63.977394, -26.7572, -9.7125, -64.0031, -23.412498, -10.4337, -68.371895, -23.412498, -9.7081995, -63.977394, -26.7572, -9.7125, -64.0031, -26.7572, -10.4337, -68.371895, -23.412498, -10.4337, -68.371895, -3.3446002, -10.4337, -68.371895, -0.0020999908, -10.5633, -70.6314, 0, -10.4337, -68.371895, -3.3446002, -10.4337, -68.371895, -3.3514, -10.5471, -70.669, -0.0020999908, -10.5633, -70.6314, 23.412498, 0, -3.3446002, 26.7577, -0.6956, 0, 23.412498, 0, 0, 23.412498, 0, -3.3446002, 26.7577, -0.4894, -3.3446002, 26.7577, -0.6956, 0, 23.412498, 0, -6.6893, 26.7577, -0.4894, -3.3446002, 23.412498, 0, -3.3446002, 23.412498, 0, -6.6893, 26.7577, -0.3464, -6.6893, 26.7577, -0.4894, -3.3446002, 23.412498, -1.0924, -23.412498, 26.7582, -1.0924, -20.0679, 23.412498, -1.0924, -20.0679, 23.412498, -1.0924, -23.412498, 26.7582, -1.0924, -23.412498, 26.7582, -1.0924, -20.0679, 23.412498, 0, -10.033899, 26.7577, -0.3464, -6.6893, 23.412498, 0, -6.6893, 23.412498, 0, -10.033899, 26.572199, -0.4003, -10.314899, 26.7577, -0.3464, -6.6893, 23.412498, -0.35529998, 10.033899, 26.7582, -1.7623999, 10.033899, 23.412498, -1.8294, 13.378601, 23.412498, -0.456, -16.7232, 26.7577, -0.8804, -16.7232, 26.572199, -0.95549995, -13.659599, 23.412498, -1.1158999, -26.7572, 26.7582, -1.0924, -23.412498, 23.412498, -1.0924, -23.412498, 23.412498, -1.1158999, -26.7572, 26.7573, -1.3222, -26.7572, 26.7582, -1.0924, -23.412498, 23.412498, -2.2719998, 23.3902, 26.7582, -2.7837, 26.6955, 23.412498, -2.2194998, 26.781, 23.412498, -2.2719998, 23.3902, 26.7582, -2.2719998, 23.3902, 26.7582, -2.7837, 26.6955, 23.412498, 0, -13.378599, 26.572199, -0.4003, -10.314899, 23.412498, 0, -10.033899, 23.412498, 0, -13.378599, 26.572199, -0.95549995, -13.659599, 26.572199, -0.4003, -10.314899, 23.412498, 0, 0, 26.7577, -0.6956, 0, 26.7577, -0.73109996, 3.3446, 23.412498, -1.0924, -20.0679, 26.7582, -1.0924, -20.0679, 26.7577, -0.8804, -16.7232, 52.997097, -8.1437, -6.6893, 56.213997, -8.7052, -3.3446002, 52.997097, -8.1437, -3.3446002, 56.173397, -8.6981, 0, 52.997097, -8.1437, -3.3446002, 56.213997, -8.7052, -3.3446002, 56.173397, -8.6981, 0, 52.997097, -8.1437, 0, 52.997097, -8.1437, -3.3446002, 52.997097, -8.1437, -6.6893, 56.254498, -8.712299, -6.6893, 56.213997, -8.7052, -3.3446002, 56.254498, -8.712299, -6.6893, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, -10.033899, 59.1829, -9.2234, 0, 56.173397, -8.6981, 0, 56.213997, -8.7052, -3.3446002, 56.254498, -8.712299, -6.6893, 59.216, -9.229199, -3.3446002, 56.213997, -8.7052, -3.3446002, 59.1829, -9.2234, 0, 56.213997, -8.7052, -3.3446002, 59.216, -9.229199, -3.3446002, 56.1329, -8.691, 3.3446, 52.997097, -8.1437, 0, 56.173397, -8.6981, 0, 56.1329, -8.691, 3.3446, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 0, 59.1498, -9.2176, 3.3446, 56.173397, -8.6981, 0, 59.1829, -9.2234, 0, 59.1498, -9.2176, 3.3446, 56.1329, -8.691, 3.3446, 56.173397, -8.6981, 0, 56.0924, -8.684, 6.6893, 52.997097, -8.1437, 3.3446, 56.1329, -8.691, 3.3446, 56.0924, -8.684, 6.6893, 52.997097, -8.1437, 6.6893, 52.997097, -8.1437, 3.3446, 59.1167, -9.2119, 6.6893, 56.1329, -8.691, 3.3446, 59.1498, -9.2176, 3.3446, 59.1167, -9.2119, 6.6893, 56.0924, -8.684, 6.6893, 56.1329, -8.691, 3.3446, 56.0518, -8.6769, 10.033899, 52.997097, -8.1437, 6.6893, 56.0924, -8.684, 6.6893, 56.0518, -8.6769, 10.033899, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 6.6893, 59.0836, -9.2060995, 10.033899, 56.0924, -8.684, 6.6893, 59.1167, -9.2119, 6.6893, 59.0836, -9.2060995, 10.033899, 56.0518, -8.6769, 10.033899, 56.0924, -8.684, 6.6893, 56.0113, -8.6698, 13.378601, 52.997097, -8.1437, 10.033899, 56.0518, -8.6769, 10.033899, 56.0113, -8.6698, 13.378601, 52.997097, -8.1437, 13.378601, 52.997097, -8.1437, 10.033899, 59.0505, -9.2003, 13.378601, 56.0518, -8.6769, 10.033899, 59.0836, -9.2060995, 10.033899, 59.0505, -9.2003, 13.378601, 56.0113, -8.6698, 13.378601, 56.0518, -8.6769, 10.033899, 55.9708, -8.6627, 16.7232, 52.997097, -8.1437, 13.378601, 56.0113, -8.6698, 13.378601, 55.9708, -8.6627, 16.7232, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 13.378601, 59.017498, -9.1945, 16.7232, 56.0113, -8.6698, 13.378601, 59.0505, -9.2003, 13.378601, 59.017498, -9.1945, 16.7232, 55.9708, -8.6627, 16.7232, 56.0113, -8.6698, 13.378601, 55.930298, -8.6557, 20.0679, 52.997097, -8.1437, 16.7232, 55.9708, -8.6627, 16.7232, 55.930298, -8.6557, 20.0679, 52.997097, -8.1437, 20.0679, 52.997097, -8.1437, 16.7232, 58.984398, -9.1888, 20.0679, 55.9708, -8.6627, 16.7232, 59.017498, -9.1945, 16.7232, 58.984398, -9.1888, 20.0679, 55.930298, -8.6557, 20.0679, 55.9708, -8.6627, 16.7232, 55.889698, -8.6486, 23.412498, 52.997097, -8.1437, 20.0679, 55.930298, -8.6557, 20.0679, 55.889698, -8.6486, 23.412498, 52.997097, -8.1437, 23.412498, 52.997097, -8.1437, 20.0679, 55.849197, -8.6414995, 26.7572, 52.997097, -8.1437, 23.412498, 55.889698, -8.6486, 23.412498, 55.849197, -8.6414995, 26.7572, 52.997097, -8.1437, 26.7572, 52.997097, -8.1437, 23.412498, 58.918198, -9.177199, 26.7572, 55.849197, -8.6414995, 26.7572, 55.889698, -8.6486, 23.412498, 58.951298, -9.183, 23.412498, 55.889698, -8.6486, 23.412498, 55.930298, -8.6557, 20.0679, 58.918198, -9.177199, 26.7572, 55.889698, -8.6486, 23.412498, 58.951298, -9.183, 23.412498, 58.951298, -9.183, 23.412498, 55.930298, -8.6557, 20.0679, 58.984398, -9.1888, 20.0679, 62.4636, -9.7961, 26.7572, 58.918198, -9.177199, 26.7572, 58.951298, -9.183, 23.412498, 62.4577, -9.795, 23.412498, 58.951298, -9.183, 23.412498, 58.984398, -9.1888, 20.0679, 62.4636, -9.7961, 26.7572, 58.951298, -9.183, 23.412498, 62.4577, -9.795, 23.412498, 66.1166, -10.4337, 23.412498, 62.4636, -9.7961, 26.7572, 62.4577, -9.795, 23.412498, 66.1166, -10.4337, 23.412498, 66.1166, -10.4337, 26.7572, 62.4636, -9.7961, 26.7572, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 23.412498, 62.4577, -9.795, 23.412498, 62.4577, -9.795, 23.412498, 58.984398, -9.1888, 20.0679, 62.451897, -9.794, 20.0679, 66.1166, -10.4337, 20.0679, 62.4577, -9.795, 23.412498, 62.451897, -9.794, 20.0679, 62.451897, -9.794, 20.0679, 58.984398, -9.1888, 20.0679, 59.017498, -9.1945, 16.7232, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 20.0679, 62.451897, -9.794, 20.0679, 62.451897, -9.794, 20.0679, 59.017498, -9.1945, 16.7232, 62.446, -9.793, 16.7232, 66.1166, -10.4337, 16.7232, 62.451897, -9.794, 20.0679, 62.446, -9.793, 16.7232, 62.446, -9.793, 16.7232, 59.017498, -9.1945, 16.7232, 59.0505, -9.2003, 13.378601, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 16.7232, 62.446, -9.793, 16.7232, 62.446, -9.793, 16.7232, 59.0505, -9.2003, 13.378601, 62.440098, -9.792, 13.378601, 66.1166, -10.4337, 13.378601, 62.446, -9.793, 16.7232, 62.440098, -9.792, 13.378601, 62.440098, -9.792, 13.378601, 59.0505, -9.2003, 13.378601, 59.0836, -9.2060995, 10.033899, 66.1166, -10.4337, 10.033899, 66.1166, -10.4337, 13.378601, 62.440098, -9.792, 13.378601, 62.440098, -9.792, 13.378601, 59.0836, -9.2060995, 10.033899, 62.4343, -9.790899, 10.033899, 66.1166, -10.4337, 10.033899, 62.440098, -9.792, 13.378601, 62.4343, -9.790899, 10.033899, 62.4343, -9.790899, 10.033899, 59.0836, -9.2060995, 10.033899, 59.1167, -9.2119, 6.6893, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 10.033899, 62.4343, -9.790899, 10.033899, 62.4343, -9.790899, 10.033899, 59.1167, -9.2119, 6.6893, 62.4284, -9.7899, 6.6893, 66.1166, -10.4337, 6.6893, 62.4343, -9.790899, 10.033899, 62.4284, -9.7899, 6.6893, 62.4284, -9.7899, 6.6893, 59.1167, -9.2119, 6.6893, 59.1498, -9.2176, 3.3446, 66.1166, -10.4337, 3.3446, 66.1166, -10.4337, 6.6893, 62.4284, -9.7899, 6.6893, 62.4284, -9.7899, 6.6893, 59.1498, -9.2176, 3.3446, 62.422497, -9.788899, 3.3446, 66.1166, -10.4337, 3.3446, 62.4284, -9.7899, 6.6893, 62.422497, -9.788899, 3.3446, 62.422497, -9.788899, 3.3446, 59.1498, -9.2176, 3.3446, 59.1829, -9.2234, 0, 66.1166, -10.4337, 0, 66.1166, -10.4337, 3.3446, 62.422497, -9.788899, 3.3446, 62.422497, -9.788899, 3.3446, 59.1829, -9.2234, 0, 62.4167, -9.7879, 0, 66.1166, -10.4337, 0, 62.422497, -9.788899, 3.3446, 62.4167, -9.7879, 0, 62.4167, -9.7879, 0, 59.1829, -9.2234, 0, 59.216, -9.229199, -3.3446002, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, 0, 62.4167, -9.7879, 0, 62.4167, -9.7879, 0, 59.216, -9.229199, -3.3446002, 62.410797, -9.786799, -3.3446002, 66.1166, -10.4337, -3.3446002, 62.4167, -9.7879, 0, 62.410797, -9.786799, -3.3446002, 59.248997, -9.235, -6.6893, 62.410797, -9.786799, -3.3446002, 59.216, -9.229199, -3.3446002, 62.4049, -9.7858, -6.6893, 66.1166, -10.4337, -3.3446002, 62.410797, -9.786799, -3.3446002, 59.248997, -9.235, -6.6893, 62.4049, -9.7858, -6.6893, 62.410797, -9.786799, -3.3446002, 62.4049, -9.7858, -6.6893, 66.1166, -10.4337, -6.6893, 66.1166, -10.4337, -3.3446002, 56.254498, -8.712299, -6.6893, 59.248997, -9.235, -6.6893, 59.216, -9.229199, -3.3446002, 66.1166, -10.4337, -6.6893, 62.4049, -9.7858, -6.6893, 62.399, -9.7848, -10.033899, 66.1166, -10.4337, -6.6893, 62.399, -9.7848, -10.033899, 66.1166, -10.4337, -10.033899, 62.4049, -9.7858, -6.6893, 59.248997, -9.235, -6.6893, 59.282097, -9.2407, -10.033899, 62.4049, -9.7858, -6.6893, 59.282097, -9.2407, -10.033899, 62.399, -9.7848, -10.033899, 59.248997, -9.235, -6.6893, 56.254498, -8.712299, -6.6893, 56.295, -8.719299, -10.033899, 59.248997, -9.235, -6.6893, 56.295, -8.719299, -10.033899, 59.282097, -9.2407, -10.033899, 56.254498, -8.712299, -6.6893, 52.997097, -8.1437, -10.033899, 56.295, -8.719299, -10.033899, 52.997097, -8.1437, -10.033899, 56.3355, -8.726399, -13.378599, 56.295, -8.719299, -10.033899, 56.3355, -8.726399, -13.378599, 59.282097, -9.2407, -10.033899, 56.295, -8.719299, -10.033899, 52.997097, -8.1437, -10.033899, 52.997097, -8.1437, -13.378599, 56.3355, -8.726399, -13.378599, 59.315197, -9.2465, -13.378599, 62.399, -9.7848, -10.033899, 59.282097, -9.2407, -10.033899, 56.3355, -8.726399, -13.378599, 59.315197, -9.2465, -13.378599, 59.282097, -9.2407, -10.033899, 52.997097, -8.1437, -13.378599, 56.3761, -8.7335, -16.7232, 56.3355, -8.726399, -13.378599, 56.3761, -8.7335, -16.7232, 59.315197, -9.2465, -13.378599, 56.3355, -8.726399, -13.378599, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -16.7232, 56.3761, -8.7335, -16.7232, 59.315197, -9.2465, -13.378599, 62.3932, -9.7838, -13.378599, 62.399, -9.7848, -10.033899, 62.3932, -9.7838, -13.378599, 66.1166, -10.4337, -10.033899, 62.399, -9.7848, -10.033899, 62.3932, -9.7838, -13.378599, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -10.033899, 59.348297, -9.252299, -16.7232, 62.3932, -9.7838, -13.378599, 59.315197, -9.2465, -13.378599, 56.3761, -8.7335, -16.7232, 59.348297, -9.252299, -16.7232, 59.315197, -9.2465, -13.378599, 62.3873, -9.7827, -16.7232, 66.1166, -10.4337, -13.378599, 62.3932, -9.7838, -13.378599, 59.348297, -9.252299, -16.7232, 62.3873, -9.7827, -16.7232, 62.3932, -9.7838, -13.378599, 62.3873, -9.7827, -16.7232, 66.1166, -10.4337, -16.7232, 66.1166, -10.4337, -13.378599, 56.4166, -8.7406, -20.0679, 59.348297, -9.252299, -16.7232, 56.3761, -8.7335, -16.7232, 52.997097, -8.1437, -16.7232, 56.4166, -8.7406, -20.0679, 56.3761, -8.7335, -16.7232, 52.997097, -8.1437, -16.7232, 52.997097, -8.1437, -20.0679, 56.4166, -8.7406, -20.0679, 52.997097, -8.1437, -20.0679, 56.4571, -8.7476, -23.412498, 56.4166, -8.7406, -20.0679, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -23.412498, 56.4571, -8.7476, -23.412498, 56.4166, -8.7406, -20.0679, 59.381397, -9.2581, -20.0679, 59.348297, -9.252299, -16.7232, 56.4571, -8.7476, -23.412498, 59.381397, -9.2581, -20.0679, 56.4166, -8.7406, -20.0679, 59.381397, -9.2581, -20.0679, 62.3873, -9.7827, -16.7232, 59.348297, -9.252299, -16.7232, 56.4571, -8.7476, -23.412498, 59.414497, -9.2638, -23.412498, 59.381397, -9.2581, -20.0679, 62.381397, -9.7817, -20.0679, 66.1166, -10.4337, -16.7232, 62.3873, -9.7827, -16.7232, 59.381397, -9.2581, -20.0679, 62.381397, -9.7817, -20.0679, 62.3873, -9.7827, -16.7232, 59.414497, -9.2638, -23.412498, 62.381397, -9.7817, -20.0679, 59.381397, -9.2581, -20.0679, 62.381397, -9.7817, -20.0679, 66.1166, -10.4337, -20.0679, 66.1166, -10.4337, -16.7232, 59.414497, -9.2638, -23.412498, 62.3756, -9.7807, -23.412498, 62.381397, -9.7817, -20.0679, 62.3756, -9.7807, -23.412498, 66.1166, -10.4337, -20.0679, 62.381397, -9.7817, -20.0679, 62.3756, -9.7807, -23.412498, 66.1166, -10.4337, -23.412498, 66.1166, -10.4337, -20.0679, 66.1166, -10.4572, -26.7572, 68.958595, -10.4337, -23.412498, 66.1166, -10.4337, -23.412498, 66.1166, -10.4572, -26.7572, 68.9481, -10.4572, -26.7572, 68.958595, -10.4337, -23.412498, 68.9481, -10.4572, -26.7572, 72.2025, -10.4337, -23.412498, 68.958595, -10.4337, -23.412498, 68.9481, -10.4572, -26.7572, 72.2092, -10.4572, -26.7572, 72.2025, -10.4337, -23.412498, 72.2092, -10.4572, -26.7572, 75.7165, -10.4337, -23.412498, 72.2025, -10.4337, -23.412498, 72.2092, -10.4572, -26.7572, 75.6957, -10.4572, -26.7572, 75.7165, -10.4337, -23.412498, 75.6957, -10.4572, -26.7572, 79.236, -10.4337, -23.412498, 75.7165, -10.4337, -23.412498, 75.6957, -10.4572, -26.7572, 79.236, -10.4572, -26.7572, 79.236, -10.4337, -23.412498, 39.877598, -3.6573, -13.378599, 42.9754, -4.7166, -10.033899, 39.877598, -3.6573, -10.033899, 39.877598, -3.6573, -13.378599, 42.951797, -4.7086, -13.378599, 42.9754, -4.7166, -10.033899, 42.951797, -4.7086, -13.378599, 46.3474, -5.8697, -10.033899, 42.9754, -4.7166, -10.033899, 42.951797, -4.7086, -13.378599, 39.877598, -3.6573, -13.378599, 39.877598, -3.6573, -16.7232, 42.951797, -4.7086, -13.378599, 46.326397, -5.8626, -13.378599, 46.3474, -5.8697, -10.033899, 46.326397, -5.8626, -13.378599, 49.4666, -6.9364, -10.033899, 46.3474, -5.8697, -10.033899, 42.951797, -4.7086, -13.378599, 39.877598, -3.6573, -16.7232, 42.9282, -4.7005, -16.7232, 46.326397, -5.8626, -13.378599, 42.951797, -4.7086, -13.378599, 42.9282, -4.7005, -16.7232, 39.877598, -3.6573, -16.7232, 42.9046, -4.6924, -20.0679, 42.9282, -4.7005, -16.7232, 39.877598, -3.6573, -16.7232, 39.877598, -3.6573, -20.0679, 42.9046, -4.6924, -20.0679, 46.326397, -5.8626, -13.378599, 42.9282, -4.7005, -16.7232, 46.3055, -5.8554, -16.7232, 42.9046, -4.6924, -20.0679, 46.3055, -5.8554, -16.7232, 42.9282, -4.7005, -16.7232, 46.326397, -5.8626, -13.378599, 49.4568, -6.9329996, -13.378599, 49.4666, -6.9364, -10.033899, 49.4568, -6.9329996, -13.378599, 46.326397, -5.8626, -13.378599, 46.3055, -5.8554, -16.7232, 49.4568, -6.9329996, -13.378599, 52.997097, -8.1437, -10.033899, 49.4666, -6.9364, -10.033899, 49.4568, -6.9329996, -13.378599, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -10.033899, 39.877598, -3.6573, -20.0679, 42.881, -4.6844, -23.412498, 42.9046, -4.6924, -20.0679, 39.877598, -3.6573, -20.0679, 39.877598, -3.6573, -23.412498, 42.881, -4.6844, -23.412498, 42.881, -4.6844, -23.412498, 46.2845, -5.8482, -20.0679, 42.9046, -4.6924, -20.0679, 42.9046, -4.6924, -20.0679, 46.2845, -5.8482, -20.0679, 46.3055, -5.8554, -16.7232, 42.881, -4.6844, -23.412498, 46.2636, -5.8410997, -23.412498, 46.2845, -5.8482, -20.0679, 52.997097, -8.1437, -13.378599, 49.4568, -6.9329996, -13.378599, 49.447, -6.9297, -16.7232, 49.4568, -6.9329996, -13.378599, 46.3055, -5.8554, -16.7232, 49.447, -6.9297, -16.7232, 46.2845, -5.8482, -20.0679, 49.447, -6.9297, -16.7232, 46.3055, -5.8554, -16.7232, 52.997097, -8.1437, -13.378599, 49.447, -6.9297, -16.7232, 52.997097, -8.1437, -16.7232, 46.2636, -5.8410997, -23.412498, 49.4372, -6.9263997, -20.0679, 46.2845, -5.8482, -20.0679, 46.2845, -5.8482, -20.0679, 49.4372, -6.9263997, -20.0679, 49.447, -6.9297, -16.7232, 49.4372, -6.9263997, -20.0679, 52.997097, -8.1437, -16.7232, 49.447, -6.9297, -16.7232, 46.2636, -5.8410997, -23.412498, 49.427498, -6.923, -23.412498, 49.4372, -6.9263997, -20.0679, 49.4372, -6.9263997, -20.0679, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -16.7232, 49.427498, -6.923, -23.412498, 52.997097, -8.1437, -20.0679, 49.4372, -6.9263997, -20.0679, 49.427498, -6.923, -23.412498, 52.997097, -8.1437, -23.412498, 52.997097, -8.1437, -20.0679, 20.0679, -8.1437, 52.156597, 16.7232, -8.658199, 55.0947, 16.7232, -8.1437, 52.156597, 13.378599, -8.658199, 55.0947, 16.7232, -8.1437, 52.156597, 16.7232, -8.658199, 55.0947, 13.378599, -8.658199, 55.0947, 13.378599, -8.1437, 52.156597, 16.7232, -8.1437, 52.156597, 20.0679, -8.1437, 52.156597, 20.0679, -8.658199, 55.0947, 16.7232, -8.658199, 55.0947, 20.0679, -8.658199, 55.0947, 20.0679, -8.1437, 52.156597, 23.412498, -8.1437, 52.156597, 13.378599, -9.1788, 58.067497, 13.378599, -8.658199, 55.0947, 16.7232, -8.658199, 55.0947, 20.0679, -8.658199, 55.0947, 16.7232, -9.1788, 58.067497, 16.7232, -8.658199, 55.0947, 13.378599, -9.1788, 58.067497, 16.7232, -8.658199, 55.0947, 16.7232, -9.1788, 58.067497, 10.033899, -8.658199, 55.0947, 13.378599, -8.1437, 52.156597, 13.378599, -8.658199, 55.0947, 10.033899, -8.658199, 55.0947, 10.033899, -8.1437, 52.156597, 13.378599, -8.1437, 52.156597, 10.033899, -9.1788, 58.067497, 13.378599, -8.658199, 55.0947, 13.378599, -9.1788, 58.067497, 10.033899, -9.1788, 58.067497, 10.033899, -8.658199, 55.0947, 13.378599, -8.658199, 55.0947, 6.6893, -8.658199, 55.0947, 10.033899, -8.1437, 52.156597, 10.033899, -8.658199, 55.0947, 6.6893, -8.658199, 55.0947, 6.6893, -8.1437, 52.156597, 10.033899, -8.1437, 52.156597, 6.6893, -9.1788, 58.067497, 10.033899, -8.658199, 55.0947, 10.033899, -9.1788, 58.067497, 6.6893, -9.1788, 58.067497, 6.6893, -8.658199, 55.0947, 10.033899, -8.658199, 55.0947, 3.3446, -8.658199, 55.0947, 6.6893, -8.1437, 52.156597, 6.6893, -8.658199, 55.0947, 3.3446, -8.658199, 55.0947, 3.3446, -8.1437, 52.156597, 6.6893, -8.1437, 52.156597, 3.3446, -9.1788, 58.067497, 6.6893, -8.658199, 55.0947, 6.6893, -9.1788, 58.067497, 3.3446, -9.1788, 58.067497, 3.3446, -8.658199, 55.0947, 6.6893, -8.658199, 55.0947, 0, -8.658199, 55.0947, 3.3446, -8.1437, 52.156597, 3.3446, -8.658199, 55.0947, 0, -8.658199, 55.0947, 0, -8.1437, 52.156597, 3.3446, -8.1437, 52.156597, 0, -9.1788, 58.067497, 3.3446, -8.658199, 55.0947, 3.3446, -9.1788, 58.067497, 0, -9.1788, 58.067497, 0, -8.658199, 55.0947, 3.3446, -8.658199, 55.0947, -3.3446002, -8.658199, 55.0947, 0, -8.1437, 52.156597, 0, -8.658199, 55.0947, -3.3446002, -8.658199, 55.0947, -3.3446002, -8.1437, 52.156597, 0, -8.1437, 52.156597, -3.3446002, -9.1788, 58.067497, 0, -8.658199, 55.0947, 0, -9.1788, 58.067497, -3.3446002, -9.1788, 58.067497, -3.3446002, -8.658199, 55.0947, 0, -8.658199, 55.0947, -6.6893, -8.658199, 55.0947, -3.3446002, -8.1437, 52.156597, -3.3446002, -8.658199, 55.0947, -6.6893, -8.658199, 55.0947, -6.6893, -8.1437, 52.156597, -3.3446002, -8.1437, 52.156597, -6.6893, -9.1788, 58.067497, -3.3446002, -8.658199, 55.0947, -3.3446002, -9.1788, 58.067497, -6.6893, -9.1788, 58.067497, -6.6893, -8.658199, 55.0947, -3.3446002, -8.658199, 55.0947, -10.033899, -8.658199, 55.0947, -6.6893, -8.1437, 52.156597, -6.6893, -8.658199, 55.0947, -10.033899, -8.658199, 55.0947, -10.033899, -8.1437, 52.156597, -6.6893, -8.1437, 52.156597, -10.033899, -9.1788, 58.067497, -6.6893, -8.658199, 55.0947, -6.6893, -9.1788, 58.067497, -10.033899, -9.1788, 58.067497, -10.033899, -8.658199, 55.0947, -6.6893, -8.658199, 55.0947, -13.378601, -8.658199, 55.0947, -10.033899, -8.1437, 52.156597, -10.033899, -8.658199, 55.0947, -13.378601, -8.658199, 55.0947, -13.378601, -8.1437, 52.156597, -10.033899, -8.1437, 52.156597, -13.378601, -9.1788, 58.067497, -10.033899, -8.658199, 55.0947, -10.033899, -9.1788, 58.067497, -13.378601, -9.1788, 58.067497, -13.378601, -8.658199, 55.0947, -10.033899, -8.658199, 55.0947, -16.7232, -8.658199, 55.0947, -13.378601, -8.1437, 52.156597, -13.378601, -8.658199, 55.0947, -16.7232, -8.658199, 55.0947, -16.7232, -8.1437, 52.156597, -13.378601, -8.1437, 52.156597, -16.7232, -9.1788, 58.067497, -13.378601, -8.658199, 55.0947, -13.378601, -9.1788, 58.067497, -16.7232, -9.1788, 58.067497, -16.7232, -8.658199, 55.0947, -13.378601, -8.658199, 55.0947, -20.0679, -8.658199, 55.0947, -16.7232, -8.1437, 52.156597, -16.7232, -8.658199, 55.0947, -20.0679, -8.658199, 55.0947, -20.0679, -8.1437, 52.156597, -16.7232, -8.1437, 52.156597, -20.0679, -9.1788, 58.067497, -16.7232, -8.658199, 55.0947, -16.7232, -9.1788, 58.067497, -20.0679, -9.1788, 58.067497, -20.0679, -8.658199, 55.0947, -16.7232, -8.658199, 55.0947, -23.412498, -8.658199, 55.0947, -20.0679, -8.1437, 52.156597, -20.0679, -8.658199, 55.0947, -23.412498, -8.658199, 55.0947, -23.412498, -8.1437, 52.156597, -20.0679, -8.1437, 52.156597, -26.7572, -8.658199, 55.0947, -23.412498, -8.1437, 52.156597, -23.412498, -8.658199, 55.0947, -26.7572, -8.658199, 55.0947, -26.7572, -8.1437, 52.156597, -23.412498, -8.1437, 52.156597, -26.7572, -9.1788, 58.067497, -26.7572, -8.658199, 55.0947, -23.412498, -8.658199, 55.0947, -23.412498, -9.1788, 58.067497, -23.412498, -8.658199, 55.0947, -20.0679, -8.658199, 55.0947, -26.7572, -9.1788, 58.067497, -23.412498, -8.658199, 55.0947, -23.412498, -9.1788, 58.067497, -23.412498, -9.1788, 58.067497, -20.0679, -8.658199, 55.0947, -20.0679, -9.1788, 58.067497, -26.7572, -9.8364, 61.822598, -26.7572, -9.1788, 58.067497, -23.412498, -9.1788, 58.067497, -23.412498, -9.8364, 61.822598, -23.412498, -9.1788, 58.067497, -20.0679, -9.1788, 58.067497, -26.7572, -9.8364, 61.822598, -23.412498, -9.1788, 58.067497, -23.412498, -9.8364, 61.822598, -23.412498, -10.4337, 65.2334, -26.7572, -9.8364, 61.822598, -23.412498, -9.8364, 61.822598, -23.412498, -10.4337, 65.2334, -26.7572, -10.4337, 65.2334, -26.7572, -9.8364, 61.822598, -20.0679, -10.4337, 65.2334, -23.412498, -10.4337, 65.2334, -23.412498, -9.8364, 61.822598, -23.412498, -9.8364, 61.822598, -20.0679, -9.1788, 58.067497, -20.0679, -9.8364, 61.822598, -20.0679, -10.4337, 65.2334, -23.412498, -9.8364, 61.822598, -20.0679, -9.8364, 61.822598, -20.0679, -9.8364, 61.822598, -20.0679, -9.1788, 58.067497, -16.7232, -9.1788, 58.067497, -16.7232, -10.4337, 65.2334, -20.0679, -10.4337, 65.2334, -20.0679, -9.8364, 61.822598, -20.0679, -9.8364, 61.822598, -16.7232, -9.1788, 58.067497, -16.7232, -9.8364, 61.822598, -16.7232, -10.4337, 65.2334, -20.0679, -9.8364, 61.822598, -16.7232, -9.8364, 61.822598, -16.7232, -9.8364, 61.822598, -16.7232, -9.1788, 58.067497, -13.378601, -9.1788, 58.067497, -13.378601, -10.4337, 65.2334, -16.7232, -10.4337, 65.2334, -16.7232, -9.8364, 61.822598, -16.7232, -9.8364, 61.822598, -13.378601, -9.1788, 58.067497, -13.378601, -9.8364, 61.822598, -13.378601, -10.4337, 65.2334, -16.7232, -9.8364, 61.822598, -13.378601, -9.8364, 61.822598, -13.378601, -9.8364, 61.822598, -13.378601, -9.1788, 58.067497, -10.033899, -9.1788, 58.067497, -10.033899, -10.4337, 65.2334, -13.378601, -10.4337, 65.2334, -13.378601, -9.8364, 61.822598, -13.378601, -9.8364, 61.822598, -10.033899, -9.1788, 58.067497, -10.033899, -9.8364, 61.822598, -10.033899, -10.4337, 65.2334, -13.378601, -9.8364, 61.822598, -10.033899, -9.8364, 61.822598, -10.033899, -9.8364, 61.822598, -10.033899, -9.1788, 58.067497, -6.6893, -9.1788, 58.067497, -6.6893, -10.4337, 65.2334, -10.033899, -10.4337, 65.2334, -10.033899, -9.8364, 61.822598, -10.033899, -9.8364, 61.822598, -6.6893, -9.1788, 58.067497, -6.6893, -9.8364, 61.822598, -6.6893, -10.4337, 65.2334, -10.033899, -9.8364, 61.822598, -6.6893, -9.8364, 61.822598, -6.6893, -9.8364, 61.822598, -6.6893, -9.1788, 58.067497, -3.3446002, -9.1788, 58.067497, -3.3446002, -10.4337, 65.2334, -6.6893, -10.4337, 65.2334, -6.6893, -9.8364, 61.822598, -6.6893, -9.8364, 61.822598, -3.3446002, -9.1788, 58.067497, -3.3446002, -9.8364, 61.822598, -3.3446002, -10.4337, 65.2334, -6.6893, -9.8364, 61.822598, -3.3446002, -9.8364, 61.822598, -3.3446002, -9.8364, 61.822598, -3.3446002, -9.1788, 58.067497, 0, -9.1788, 58.067497, 0, -10.4337, 65.2334, -3.3446002, -10.4337, 65.2334, -3.3446002, -9.8364, 61.822598, -3.3446002, -9.8364, 61.822598, 0, -9.1788, 58.067497, 0, -9.8364, 61.822598, 0, -10.4337, 65.2334, -3.3446002, -9.8364, 61.822598, 0, -9.8364, 61.822598, 0, -9.8364, 61.822598, 0, -9.1788, 58.067497, 3.3446, -9.1788, 58.067497, 3.3446, -10.4337, 65.2334, 0, -10.4337, 65.2334, 0, -9.8364, 61.822598, 0, -9.8364, 61.822598, 3.3446, -9.1788, 58.067497, 3.3446, -9.8364, 61.822598, 3.3446, -10.4337, 65.2334, 0, -9.8364, 61.822598, 3.3446, -9.8364, 61.822598, 3.3446, -9.8364, 61.822598, 3.3446, -9.1788, 58.067497, 6.6893, -9.1788, 58.067497, 6.6893, -10.4337, 65.2334, 3.3446, -10.4337, 65.2334, 3.3446, -9.8364, 61.822598, 3.3446, -9.8364, 61.822598, 6.6893, -9.1788, 58.067497, 6.6893, -9.8364, 61.822598, 6.6893, -10.4337, 65.2334, 3.3446, -9.8364, 61.822598, 6.6893, -9.8364, 61.822598, 6.6893, -9.8364, 61.822598, 6.6893, -9.1788, 58.067497, 10.033899, -9.1788, 58.067497, 10.033899, -10.4337, 65.2334, 6.6893, -10.4337, 65.2334, 6.6893, -9.8364, 61.822598, 6.6893, -9.8364, 61.822598, 10.033899, -9.1788, 58.067497, 10.033899, -9.8364, 61.822598, 10.033899, -10.4337, 65.2334, 6.6893, -9.8364, 61.822598, 10.033899, -9.8364, 61.822598, 10.033899, -9.8364, 61.822598, 10.033899, -9.1788, 58.067497, 13.378599, -9.1788, 58.067497, 13.378599, -10.4337, 65.2334, 10.033899, -10.4337, 65.2334, 10.033899, -9.8364, 61.822598, 10.033899, -9.8364, 61.822598, 13.378599, -9.1788, 58.067497, 13.378599, -9.8364, 61.822598, 13.378599, -10.4337, 65.2334, 10.033899, -9.8364, 61.822598, 13.378599, -9.8364, 61.822598, 13.378599, -9.8364, 61.822598, 13.378599, -9.1788, 58.067497, 16.7232, -9.1788, 58.067497, 16.7232, -10.4337, 65.2334, 13.378599, -10.4337, 65.2334, 13.378599, -9.8364, 61.822598, 13.378599, -9.8364, 61.822598, 16.7232, -9.1788, 58.067497, 16.7232, -9.8364, 61.822598, 16.7232, -10.4337, 65.2334, 13.378599, -9.8364, 61.822598, 16.7232, -9.8364, 61.822598, 20.0679, -9.1788, 58.067497, 16.7232, -9.8364, 61.822598, 16.7232, -9.1788, 58.067497, 20.0679, -9.8364, 61.822598, 16.7232, -10.4337, 65.2334, 16.7232, -9.8364, 61.822598, 20.0679, -9.1788, 58.067497, 20.0679, -9.8364, 61.822598, 16.7232, -9.8364, 61.822598, 20.0679, -9.8364, 61.822598, 20.0679, -10.4337, 65.2334, 16.7232, -10.4337, 65.2334, 20.0679, -8.658199, 55.0947, 20.0679, -9.1788, 58.067497, 16.7232, -9.1788, 58.067497, 20.0679, -10.4337, 65.2334, 20.0679, -9.8364, 61.822598, 23.412498, -9.8364, 61.822598, 20.0679, -10.4337, 65.2334, 23.412498, -9.8364, 61.822598, 23.412498, -10.4337, 65.2334, 26.7582, -9.8364, 61.822598, 23.412498, -10.4337, 65.2334, 23.412498, -9.8364, 61.822598, 26.7582, -9.8364, 61.822598, 26.7582, -10.4337, 65.2334, 23.412498, -10.4337, 65.2334, 26.7582, -9.1788, 58.067497, 26.7582, -9.8364, 61.822598, 23.412498, -9.8364, 61.822598, 20.0679, -9.8364, 61.822598, 23.412498, -9.1788, 58.067497, 23.412498, -9.8364, 61.822598, 26.7582, -9.1788, 58.067497, 23.412498, -9.8364, 61.822598, 23.412498, -9.1788, 58.067497, 20.0679, -9.8364, 61.822598, 20.0679, -9.1788, 58.067497, 23.412498, -9.1788, 58.067497, 26.7582, -8.658199, 55.0947, 26.7582, -9.1788, 58.067497, 23.412498, -9.1788, 58.067497, 20.0679, -9.1788, 58.067497, 20.0679, -8.658199, 55.0947, 23.412498, -8.658199, 55.0947, 20.0679, -9.1788, 58.067497, 23.412498, -8.658199, 55.0947, 23.412498, -9.1788, 58.067497, 26.7582, -8.658199, 55.0947, 23.412498, -9.1788, 58.067497, 23.412498, -8.658199, 55.0947, 20.0679, -8.658199, 55.0947, 23.412498, -8.1437, 52.156597, 23.412498, -8.658199, 55.0947, 23.412498, -8.1437, 52.156597, 26.7582, -8.658199, 55.0947, 23.412498, -8.658199, 55.0947, 23.412498, -8.1437, 52.156597, 26.7582, -8.1437, 52.156597, 26.7582, -8.658199, 55.0947, 20.0679, -3.8518999, 39.456898, 16.7232, -4.9595, 42.7343, 16.7232, -3.8518999, 39.456898, 13.378599, -4.9595, 42.7343, 16.7232, -3.8518999, 39.456898, 16.7232, -4.9595, 42.7343, 13.378599, -4.9595, 42.7343, 13.378599, -3.8518999, 39.456898, 16.7232, -3.8518999, 39.456898, 20.0679, -3.8518999, 39.456898, 20.0679, -4.9595, 42.7343, 16.7232, -4.9595, 42.7343, 20.0679, -4.9595, 42.7343, 20.0679, -3.8518999, 39.456898, 23.412498, -3.8518999, 39.456898, 13.378599, -6.2019997, 46.4111, 13.378599, -4.9595, 42.7343, 16.7232, -4.9595, 42.7343, 20.0679, -4.9595, 42.7343, 16.7232, -6.2019997, 46.4111, 16.7232, -4.9595, 42.7343, 13.378599, -6.2019997, 46.4111, 16.7232, -4.9595, 42.7343, 16.7232, -6.2019997, 46.4111, 10.033899, -4.9595, 42.7343, 13.378599, -3.8518999, 39.456898, 13.378599, -4.9595, 42.7343, 10.033899, -4.9595, 42.7343, 10.033899, -3.8518999, 39.456898, 13.378599, -3.8518999, 39.456898, 10.033899, -6.2019997, 46.4111, 13.378599, -4.9595, 42.7343, 13.378599, -6.2019997, 46.4111, 10.033899, -6.2019997, 46.4111, 10.033899, -4.9595, 42.7343, 13.378599, -4.9595, 42.7343, 6.6893, -4.9595, 42.7343, 10.033899, -3.8518999, 39.456898, 10.033899, -4.9595, 42.7343, 6.6893, -4.9595, 42.7343, 6.6893, -3.8518999, 39.456898, 10.033899, -3.8518999, 39.456898, 6.6893, -6.2019997, 46.4111, 10.033899, -4.9595, 42.7343, 10.033899, -6.2019997, 46.4111, 6.6893, -6.2019997, 46.4111, 6.6893, -4.9595, 42.7343, 10.033899, -4.9595, 42.7343, 3.3446, -4.9595, 42.7343, 6.6893, -3.8518999, 39.456898, 6.6893, -4.9595, 42.7343, 3.3446, -4.9595, 42.7343, 3.3446, -3.8518999, 39.456898, 6.6893, -3.8518999, 39.456898, 3.3446, -6.2019997, 46.4111, 6.6893, -4.9595, 42.7343, 6.6893, -6.2019997, 46.4111, 3.3446, -6.2019997, 46.4111, 3.3446, -4.9595, 42.7343, 6.6893, -4.9595, 42.7343, 0, -4.9595, 42.7343, 3.3446, -3.8518999, 39.456898, 3.3446, -4.9595, 42.7343, 0, -4.9595, 42.7343, 0, -3.8518999, 39.456898, 3.3446, -3.8518999, 39.456898, 0, -6.2019997, 46.4111, 3.3446, -4.9595, 42.7343, 3.3446, -6.2019997, 46.4111, 0, -6.2019997, 46.4111, 0, -4.9595, 42.7343, 3.3446, -4.9595, 42.7343, -3.3446002, -4.9595, 42.7343, 0, -3.8518999, 39.456898, 0, -4.9595, 42.7343, -3.3446002, -4.9595, 42.7343, -3.3446002, -3.8518999, 39.456898, 0, -3.8518999, 39.456898, -3.3446002, -6.2019997, 46.4111, 0, -4.9595, 42.7343, 0, -6.2019997, 46.4111, -3.3446002, -6.2019997, 46.4111, -3.3446002, -4.9595, 42.7343, 0, -4.9595, 42.7343, -6.6893, -4.9595, 42.7343, -3.3446002, -3.8518999, 39.456898, -3.3446002, -4.9595, 42.7343, -6.6893, -4.9595, 42.7343, -6.6893, -3.8518999, 39.456898, -3.3446002, -3.8518999, 39.456898, -6.6893, -6.2019997, 46.4111, -3.3446002, -4.9595, 42.7343, -3.3446002, -6.2019997, 46.4111, -6.6893, -6.2019997, 46.4111, -6.6893, -4.9595, 42.7343, -3.3446002, -4.9595, 42.7343, -10.033899, -4.9595, 42.7343, -6.6893, -3.8518999, 39.456898, -6.6893, -4.9595, 42.7343, -10.033899, -4.9595, 42.7343, -10.033899, -3.8518999, 39.456898, -6.6893, -3.8518999, 39.456898, -10.033899, -6.2019997, 46.4111, -6.6893, -4.9595, 42.7343, -6.6893, -6.2019997, 46.4111, -10.033899, -6.2019997, 46.4111, -10.033899, -4.9595, 42.7343, -6.6893, -4.9595, 42.7343, -13.378601, -4.9595, 42.7343, -10.033899, -3.8518999, 39.456898, -10.033899, -4.9595, 42.7343, -13.378601, -4.9595, 42.7343, -13.378601, -3.8518999, 39.456898, -10.033899, -3.8518999, 39.456898, -13.378601, -6.2019997, 46.4111, -10.033899, -4.9595, 42.7343, -10.033899, -6.2019997, 46.4111, -13.378601, -6.2019997, 46.4111, -13.378601, -4.9595, 42.7343, -10.033899, -4.9595, 42.7343, -16.7232, -4.9595, 42.7343, -13.378601, -3.8518999, 39.456898, -13.378601, -4.9595, 42.7343, -16.7232, -4.9595, 42.7343, -16.7232, -3.8518999, 39.456898, -13.378601, -3.8518999, 39.456898, -16.7232, -6.2019997, 46.4111, -13.378601, -4.9595, 42.7343, -13.378601, -6.2019997, 46.4111, -16.7232, -6.2019997, 46.4111, -16.7232, -4.9595, 42.7343, -13.378601, -4.9595, 42.7343, -20.0679, -4.9595, 42.7343, -16.7232, -3.8518999, 39.456898, -16.7232, -4.9595, 42.7343, -20.0679, -4.9595, 42.7343, -20.0679, -3.8518999, 39.456898, -16.7232, -3.8518999, 39.456898, -20.0679, -6.2019997, 46.4111, -16.7232, -4.9595, 42.7343, -16.7232, -6.2019997, 46.4111, -20.0679, -6.2019997, 46.4111, -20.0679, -4.9595, 42.7343, -16.7232, -4.9595, 42.7343, -23.412498, -4.9595, 42.7343, -20.0679, -3.8518999, 39.456898, -20.0679, -4.9595, 42.7343, -23.412498, -4.9595, 42.7343, -23.412498, -3.8518999, 39.456898, -20.0679, -3.8518999, 39.456898, -26.7572, -4.9595, 42.7343, -23.412498, -3.8518999, 39.456898, -23.412498, -4.9595, 42.7343, -26.7572, -4.9595, 42.7343, -26.7572, -3.8518999, 39.456898, -23.412498, -3.8518999, 39.456898, -26.7572, -6.2019997, 46.4111, -26.7572, -4.9595, 42.7343, -23.412498, -4.9595, 42.7343, -23.412498, -6.2019997, 46.4111, -23.412498, -4.9595, 42.7343, -20.0679, -4.9595, 42.7343, -26.7572, -6.2019997, 46.4111, -23.412498, -4.9595, 42.7343, -23.412498, -6.2019997, 46.4111, -23.412498, -6.2019997, 46.4111, -20.0679, -4.9595, 42.7343, -20.0679, -6.2019997, 46.4111, -26.7572, -7.286, 49.6186, -26.7572, -6.2019997, 46.4111, -23.412498, -6.2019997, 46.4111, -23.412498, -7.286, 49.6186, -23.412498, -6.2019997, 46.4111, -20.0679, -6.2019997, 46.4111, -26.7572, -7.286, 49.6186, -23.412498, -6.2019997, 46.4111, -23.412498, -7.286, 49.6186, -23.412498, -8.1437, 52.156597, -26.7572, -7.286, 49.6186, -23.412498, -7.286, 49.6186, -23.412498, -8.1437, 52.156597, -26.7572, -8.1437, 52.156597, -26.7572, -7.286, 49.6186, -20.0679, -8.1437, 52.156597, -23.412498, -8.1437, 52.156597, -23.412498, -7.286, 49.6186, -23.412498, -7.286, 49.6186, -20.0679, -6.2019997, 46.4111, -20.0679, -7.286, 49.6186, -20.0679, -8.1437, 52.156597, -23.412498, -7.286, 49.6186, -20.0679, -7.286, 49.6186, -20.0679, -7.286, 49.6186, -20.0679, -6.2019997, 46.4111, -16.7232, -6.2019997, 46.4111, -16.7232, -8.1437, 52.156597, -20.0679, -8.1437, 52.156597, -20.0679, -7.286, 49.6186, -20.0679, -7.286, 49.6186, -16.7232, -6.2019997, 46.4111, -16.7232, -7.286, 49.6186, -16.7232, -8.1437, 52.156597, -20.0679, -7.286, 49.6186, -16.7232, -7.286, 49.6186, -16.7232, -7.286, 49.6186, -16.7232, -6.2019997, 46.4111, -13.378601, -6.2019997, 46.4111, -13.378601, -8.1437, 52.156597, -16.7232, -8.1437, 52.156597, -16.7232, -7.286, 49.6186, -16.7232, -7.286, 49.6186, -13.378601, -6.2019997, 46.4111, -13.378601, -7.286, 49.6186, -13.378601, -8.1437, 52.156597, -16.7232, -7.286, 49.6186, -13.378601, -7.286, 49.6186, -13.378601, -7.286, 49.6186, -13.378601, -6.2019997, 46.4111, -10.033899, -6.2019997, 46.4111, -10.033899, -8.1437, 52.156597, -13.378601, -8.1437, 52.156597, -13.378601, -7.286, 49.6186, -13.378601, -7.286, 49.6186, -10.033899, -6.2019997, 46.4111, -10.033899, -7.286, 49.6186, -10.033899, -8.1437, 52.156597, -13.378601, -7.286, 49.6186, -10.033899, -7.286, 49.6186, -10.033899, -7.286, 49.6186, -10.033899, -6.2019997, 46.4111, -6.6893, -6.2019997, 46.4111, -6.6893, -8.1437, 52.156597, -10.033899, -8.1437, 52.156597, -10.033899, -7.286, 49.6186, -10.033899, -7.286, 49.6186, -6.6893, -6.2019997, 46.4111, -6.6893, -7.286, 49.6186, -6.6893, -8.1437, 52.156597, -10.033899, -7.286, 49.6186, -6.6893, -7.286, 49.6186, -6.6893, -7.286, 49.6186, -6.6893, -6.2019997, 46.4111, -3.3446002, -6.2019997, 46.4111, -3.3446002, -8.1437, 52.156597, -6.6893, -8.1437, 52.156597, -6.6893, -7.286, 49.6186, -6.6893, -7.286, 49.6186, -3.3446002, -6.2019997, 46.4111, -3.3446002, -7.286, 49.6186, -3.3446002, -8.1437, 52.156597, -6.6893, -7.286, 49.6186, -3.3446002, -7.286, 49.6186, -3.3446002, -7.286, 49.6186, -3.3446002, -6.2019997, 46.4111, 0, -6.2019997, 46.4111, 0, -8.1437, 52.156597, -3.3446002, -8.1437, 52.156597, -3.3446002, -7.286, 49.6186, -3.3446002, -7.286, 49.6186, 0, -6.2019997, 46.4111, 0, -7.286, 49.6186, 0, -8.1437, 52.156597, -3.3446002, -7.286, 49.6186, 0, -7.286, 49.6186, 0, -7.286, 49.6186, 0, -6.2019997, 46.4111, 3.3446, -6.2019997, 46.4111, 3.3446, -8.1437, 52.156597, 0, -8.1437, 52.156597, 0, -7.286, 49.6186, 0, -7.286, 49.6186, 3.3446, -6.2019997, 46.4111, 3.3446, -7.286, 49.6186, 3.3446, -8.1437, 52.156597, 0, -7.286, 49.6186, 3.3446, -7.286, 49.6186, 3.3446, -7.286, 49.6186, 3.3446, -6.2019997, 46.4111, 6.6893, -6.2019997, 46.4111, 6.6893, -8.1437, 52.156597, 3.3446, -8.1437, 52.156597, 3.3446, -7.286, 49.6186, 3.3446, -7.286, 49.6186, 6.6893, -6.2019997, 46.4111, 6.6893, -7.286, 49.6186, 6.6893, -8.1437, 52.156597, 3.3446, -7.286, 49.6186, 6.6893, -7.286, 49.6186, 6.6893, -7.286, 49.6186, 6.6893, -6.2019997, 46.4111, 10.033899, -6.2019997, 46.4111, 10.033899, -8.1437, 52.156597, 6.6893, -8.1437, 52.156597, 6.6893, -7.286, 49.6186, 6.6893, -7.286, 49.6186, 10.033899, -6.2019997, 46.4111, 10.033899, -7.286, 49.6186, 10.033899, -8.1437, 52.156597, 6.6893, -7.286, 49.6186, 10.033899, -7.286, 49.6186, 10.033899, -7.286, 49.6186, 10.033899, -6.2019997, 46.4111, 13.378599, -6.2019997, 46.4111, 13.378599, -8.1437, 52.156597, 10.033899, -8.1437, 52.156597, 10.033899, -7.286, 49.6186, 10.033899, -7.286, 49.6186, 13.378599, -6.2019997, 46.4111, 13.378599, -7.286, 49.6186, 13.378599, -8.1437, 52.156597, 10.033899, -7.286, 49.6186, 13.378599, -7.286, 49.6186, 13.378599, -7.286, 49.6186, 13.378599, -6.2019997, 46.4111, 16.7232, -6.2019997, 46.4111, 16.7232, -8.1437, 52.156597, 13.378599, -8.1437, 52.156597, 13.378599, -7.286, 49.6186, 13.378599, -7.286, 49.6186, 16.7232, -6.2019997, 46.4111, 16.7232, -7.286, 49.6186, 16.7232, -8.1437, 52.156597, 13.378599, -7.286, 49.6186, 16.7232, -7.286, 49.6186, 20.0679, -6.2019997, 46.4111, 16.7232, -7.286, 49.6186, 16.7232, -6.2019997, 46.4111, 20.0679, -7.286, 49.6186, 16.7232, -8.1437, 52.156597, 16.7232, -7.286, 49.6186, 20.0679, -6.2019997, 46.4111, 20.0679, -7.286, 49.6186, 16.7232, -7.286, 49.6186, 20.0679, -7.286, 49.6186, 20.0679, -8.1437, 52.156597, 16.7232, -8.1437, 52.156597, 20.0679, -4.9595, 42.7343, 20.0679, -6.2019997, 46.4111, 16.7232, -6.2019997, 46.4111, 20.0679, -8.1437, 52.156597, 20.0679, -7.286, 49.6186, 23.412498, -7.286, 49.6186, 20.0679, -8.1437, 52.156597, 23.412498, -7.286, 49.6186, 23.412498, -8.1437, 52.156597, 26.7582, -7.286, 49.6186, 23.412498, -8.1437, 52.156597, 23.412498, -7.286, 49.6186, 26.7582, -7.286, 49.6186, 26.7582, -8.1437, 52.156597, 23.412498, -8.1437, 52.156597, 26.7582, -6.2019997, 46.4111, 26.7582, -7.286, 49.6186, 23.412498, -7.286, 49.6186, 20.0679, -7.286, 49.6186, 23.412498, -6.2019997, 46.4111, 23.412498, -7.286, 49.6186, 26.7582, -6.2019997, 46.4111, 23.412498, -7.286, 49.6186, 23.412498, -6.2019997, 46.4111, 20.0679, -7.286, 49.6186, 20.0679, -6.2019997, 46.4111, 23.412498, -6.2019997, 46.4111, 26.7582, -4.9595, 42.7343, 26.7582, -6.2019997, 46.4111, 23.412498, -6.2019997, 46.4111, 20.0679, -6.2019997, 46.4111, 20.0679, -4.9595, 42.7343, 23.412498, -4.9595, 42.7343, 20.0679, -6.2019997, 46.4111, 23.412498, -4.9595, 42.7343, 23.412498, -6.2019997, 46.4111, 26.7582, -4.9595, 42.7343, 23.412498, -6.2019997, 46.4111, 23.412498, -4.9595, 42.7343, 20.0679, -4.9595, 42.7343, 23.412498, -3.8518999, 39.456898, 23.412498, -4.9595, 42.7343, 23.412498, -3.8518999, 39.456898, 26.7582, -4.9595, 42.7343, 23.412498, -4.9595, 42.7343, 23.412498, -3.8518999, 39.456898, 26.7582, -3.8518999, 39.456898, 26.7582, -4.9595, 42.7343, -26.7572, -1.2701, 26.7572, -26.7572, -2.0054, 30.3737, -28.645699, -2.01, 28.5121, -28.645699, -2.01, 28.5121, -30.426, -2.0144, 26.7572, -26.7572, -1.2701, 26.7572, -28.447899, -2.6727, 31.969099, -28.645699, -2.01, 28.5121, -26.7572, -2.0054, 30.3737, -26.7572, -2.0054, 30.3737, -26.7572, -2.6892, 33.737698, -28.447899, -2.6727, 31.969099, -30.291101, -2.6547, 30.040998, -30.426, -2.0144, 26.7572, -28.645699, -2.01, 28.5121, -28.447899, -2.6727, 31.969099, -30.291101, -2.6547, 30.040998, -28.645699, -2.01, 28.5121, -30.291101, -2.6547, 30.040998, -32.027798, -2.6376998, 28.2244, -30.426, -2.0144, 26.7572, -32.027798, -2.6376998, 28.2244, -33.4304, -2.6239998, 26.7572, -30.426, -2.0144, 26.7572, -29.8963, -3.2444, 33.336, -30.291101, -2.6547, 30.040998, -28.447899, -2.6727, 31.969099, -26.7572, -2.6892, 33.737698, -29.8963, -3.2444, 33.336, -28.447899, -2.6727, 31.969099, -31.7465, -3.2247999, 31.3933, -32.027798, -2.6376998, 28.2244, -30.291101, -2.6547, 30.040998, -29.8963, -3.2444, 33.336, -31.7465, -3.2247999, 31.3933, -30.291101, -2.6547, 30.040998, -26.7572, -2.6892, 33.737698, -28.188799, -3.2624998, 35.128998, -29.8963, -3.2444, 33.336, -28.188799, -3.2624998, 35.128998, -26.7572, -2.6892, 33.737698, -26.7572, -3.2777, 36.6322, -29.5455, -3.8058, 36.4474, -29.8963, -3.2444, 33.336, -28.188799, -3.2624998, 35.128998, -26.7572, -3.2777, 36.6322, -29.5455, -3.8058, 36.4474, -28.188799, -3.2624998, 35.128998, -26.7572, -3.2777, 36.6322, -28.1288, -3.8291998, 37.976498, -29.5455, -3.8058, 36.4474, -28.1288, -3.8291998, 37.976498, -26.7572, -3.2777, 36.6322, -26.7572, -3.8518999, 39.456898, -29.5455, -3.8058, 36.4474, -31.247301, -3.7775998, 34.6107, -29.8963, -3.2444, 33.336, -31.247301, -3.7775998, 34.6107, -31.7465, -3.2247999, 31.3933, -29.8963, -3.2444, 33.336, -31.247301, -3.7775998, 34.6107, -33.0801, -3.7473, 32.6325, -31.7465, -3.2247999, 31.3933, -33.0801, -3.7473, 32.6325, -33.4894, -3.2064, 29.5632, -31.7465, -3.2247999, 31.3933, -31.7465, -3.2247999, 31.3933, -33.4894, -3.2064, 29.5632, -32.027798, -2.6376998, 28.2244, -33.4894, -3.2064, 29.5632, -33.4304, -2.6239998, 26.7572, -32.027798, -2.6376998, 28.2244, -33.0801, -3.7473, 32.6325, -34.806297, -3.7187998, 30.769499, -33.4894, -3.2064, 29.5632, -33.4894, -3.2064, 29.5632, -34.9099, -3.1913, 28.0716, -33.4304, -2.6239998, 26.7572, -34.806297, -3.7187998, 30.769499, -34.9099, -3.1913, 28.0716, -33.4894, -3.2064, 29.5632, -34.9099, -3.1913, 28.0716, -36.161697, -3.1780999, 26.7572, -33.4304, -2.6239998, 26.7572, -34.806297, -3.7187998, 30.769499, -36.2241, -3.6952999, 29.2393, -34.9099, -3.1913, 28.0716, -36.2241, -3.6952999, 29.2393, -36.161697, -3.1780999, 26.7572, -34.9099, -3.1913, 28.0716, -36.2241, -3.6952999, 29.2393, -37.4711, -3.6747, 27.8934, -36.161697, -3.1780999, 26.7572, -37.4711, -3.6747, 27.8934, -38.5239, -3.6573, 26.7572, -36.161697, -3.1780999, 26.7572, -50.290497, -8.1437, 26.7572, -47.4473, -7.0596, 26.7572, -49.074398, -8.1437, 28.0697, -62.057194, -10.4337, 26.7572, -58.463497, -9.7343, 26.7572, -60.611, -10.4337, 28.3335, 26.7582, -9.8364, 61.822598, 66.1166, -10.4337, 26.7572, 26.7582, -10.4337, 65.2334, 26.7582, -9.8364, 61.822598, 62.4636, -9.7961, 26.7572, 66.1166, -10.4337, 26.7572, 26.7582, -9.1788, 58.067497, 62.4636, -9.7961, 26.7572, 26.7582, -9.8364, 61.822598, 26.7582, -9.1788, 58.067497, 58.918198, -9.177199, 26.7572, 62.4636, -9.7961, 26.7572, 26.7582, -9.1788, 58.067497, 55.849197, -8.6414995, 26.7572, 58.918198, -9.177199, 26.7572, 55.849197, -8.6414995, 26.7572, 26.7582, -9.1788, 58.067497, 52.997097, -8.1437, 26.7572, 52.997097, -8.1437, 26.7572, 26.7582, -9.1788, 58.067497, 26.7582, -8.1437, 52.156597, 26.7582, -8.1437, 52.156597, 26.7582, -9.1788, 58.067497, 26.7582, -8.658199, 55.0947, 43.234898, -4.8054, 26.7572, 26.7582, -3.8518999, 39.456898, 39.877598, -3.6573, 26.7572, 46.577698, -5.9484997, 26.7572, 26.7582, -3.8518999, 39.456898, 43.234898, -4.8054, 26.7572, 46.577698, -5.9484997, 26.7572, 26.7582, -6.2019997, 46.4111, 26.7582, -3.8518999, 39.456898, 26.7582, -3.8518999, 39.456898, 26.7582, -6.2019997, 46.4111, 26.7582, -4.9595, 42.7343, 26.7582, -6.2019997, 46.4111, 46.577698, -5.9484997, 26.7572, 26.7582, -8.1437, 52.156597, 26.7582, -6.2019997, 46.4111, 26.7582, -8.1437, 52.156597, 26.7582, -7.286, 49.6186, 26.7582, -8.1437, 52.156597, 46.577698, -5.9484997, 26.7572, 49.5743, -6.9732, 26.7572, 26.7582, -8.1437, 52.156597, 49.5743, -6.9732, 26.7572, 52.997097, -8.1437, 26.7572, 29.1145, -2.5956, 26.7572, 26.7582, -3.0839999, 30.2834, 26.7582, -2.7837, 26.6955, 29.1145, -2.5956, 26.7572, 26.7582, -3.3683999, 33.680798, 26.7582, -3.0839999, 30.2834, 33.146698, -3.214, 26.7572, 26.7582, -3.3683999, 33.680798, 29.1145, -2.5956, 26.7572, 33.146698, -3.214, 26.7572, 26.7582, -3.6130998, 36.6041, 26.7582, -3.3683999, 33.680798, 26.7582, -3.6130998, 36.6041, 33.146698, -3.214, 26.7572, 39.877598, -3.6573, 26.7572, 39.877598, -3.6573, 26.7572, 33.146698, -3.214, 26.7572, 36.078598, -3.0746999, 26.7572, 26.7582, -3.6130998, 36.6041, 39.877598, -3.6573, 26.7572, 26.7582, -3.8518999, 39.456898, 26.7573, -1.7447, -30.111198, 29.3928, -1.796, -26.7572, 26.7573, -1.3222, -26.7572, 26.7572, -2.5274, -33.9013, 29.3928, -1.796, -26.7572, 26.7573, -1.7447, -30.111198, 29.3928, -1.796, -26.7572, 26.7572, -2.5274, -33.9013, 26.7572, -3.9166, -40.6287, 26.7572, -3.9166, -40.6287, 26.7572, -2.5274, -33.9013, 26.7572, -3.2101, -37.2077, 29.3928, -1.796, -26.7572, 26.7572, -3.9166, -40.6287, 36.0678, -2.9959998, -26.7572, 36.0678, -2.9959998, -26.7572, 26.7572, -3.9166, -40.6287, 39.877598, -3.6808999, -26.7572, 29.3928, -1.796, -26.7572, 36.0678, -2.9959998, -26.7572, 32.9589, -2.4371, -26.7572, 49.4036, -6.9384, -26.7572, 26.7572, -8.403, -54.500298, 52.997097, -8.1672, -26.7572, 46.2278, -5.8524, -26.7572, 26.7572, -8.403, -54.500298, 49.4036, -6.9384, -26.7572, 26.7572, -8.403, -54.500298, 46.2278, -5.8524, -26.7572, 26.7572, -3.9166, -40.6287, 26.7572, -3.9166, -40.6287, 46.2278, -5.8524, -26.7572, 42.8416, -4.6944, -26.7572, 26.7572, -3.9166, -40.6287, 42.8416, -4.6944, -26.7572, 39.877598, -3.6808999, -26.7572, 26.7572, -8.403, -54.500298, 26.7572, -3.9166, -40.6287, 26.7572, -6.2504, -47.8447, 26.7572, -6.2504, -47.8447, 26.7572, -3.9166, -40.6287, 26.7572, -5.1514, -44.446598, 26.7572, -8.403, -54.500298, 26.7572, -6.2504, -47.8447, 26.7572, -7.3550997, -51.2602, 62.356697, -9.8008995, -26.7572, 26.7572, -10.693, -68.371895, 66.1166, -10.4572, -26.7572, 59.4335, -9.2907, -26.7572, 26.7572, -10.693, -68.371895, 62.356697, -9.8008995, -26.7572, 26.7572, -10.693, -68.371895, 59.4335, -9.2907, -26.7572, 26.7572, -9.312699, -60.011097, 26.7572, -10.693, -68.371895, 26.7572, -9.312699, -60.011097, 26.7572, -9.876599, -63.426796, 26.7572, -9.312699, -60.011097, 59.4335, -9.2907, -26.7572, 56.4827, -8.7755995, -26.7572, 26.7572, -9.312699, -60.011097, 56.4827, -8.7755995, -26.7572, 26.7572, -8.881, -57.3956, 26.7572, -8.881, -57.3956, 56.4827, -8.7755995, -26.7572, 52.997097, -8.1672, -26.7572, 26.7572, -8.881, -57.3956, 52.997097, -8.1672, -26.7572, 26.7572, -8.403, -54.500298, 26.7572, -10.693, -70.330795, 66.1166, -10.4572, -26.7572, 26.7572, -10.693, -68.371895, 26.7572, -10.693, -70.330795, 68.9481, -10.4572, -26.7572, 66.1166, -10.4572, -26.7572, 26.7572, -10.693, -73.1232, 68.9481, -10.4572, -26.7572, 26.7572, -10.693, -70.330795, 26.7572, -10.693, -73.1232, 72.2092, -10.4572, -26.7572, 68.9481, -10.4572, -26.7572, 26.7572, -10.693, -73.1232, 75.6957, -10.4572, -26.7572, 72.2092, -10.4572, -26.7572, 75.6957, -10.4572, -26.7572, 26.7572, -10.693, -73.1232, 26.7572, -10.693, -82.2434, 75.6957, -10.4572, -26.7572, 26.7572, -10.693, -82.2434, 79.236, -10.4572, -26.7572, 26.7572, -10.693, -82.2434, 26.7572, -10.693, -73.1232, 26.7572, -10.693, -79.5579, 26.7572, -10.693, -79.5579, 26.7572, -10.693, -73.1232, 26.7572, -10.693, -76.193, -30.173899, -3.2568, -26.7572, -26.7572, -3.2445998, -30.4851, -26.7572, -3.0929, -26.7572, -30.173899, -3.2568, -26.7572, -26.7572, -3.3862, -33.9664, -26.7572, -3.2445998, -30.4851, -30.173899, -3.2568, -26.7572, -26.7572, -3.5244, -37.3623, -26.7572, -3.3862, -33.9664, -26.7572, -3.5244, -37.3623, -30.173899, -3.2568, -26.7572, -36.0882, -3.5405, -26.7572, -36.0882, -3.5405, -26.7572, -30.173899, -3.2568, -26.7572, -33.2719, -3.4053998, -26.7572, -26.7572, -3.5244, -37.3623, -36.0882, -3.5405, -26.7572, -26.7572, -3.6573, -40.6287, -26.7572, -3.6573, -40.6287, -36.0882, -3.5405, -26.7572, -38.5239, -3.6573, -26.7572, -41.627, -4.8403997, -26.7572, -26.7572, -3.6573, -40.6287, -38.5239, -3.6573, -26.7572, -41.627, -4.8403997, -26.7572, -26.7572, -5.9270997, -47.646698, -26.7572, -3.6573, -40.6287, -26.7572, -3.6573, -40.6287, -26.7572, -5.9270997, -47.646698, -26.7572, -4.8125997, -44.200798, -44.537098, -5.95, -26.7572, -26.7572, -5.9270997, -47.646698, -41.627, -4.8403997, -26.7572, -26.7572, -5.9270997, -47.646698, -44.537098, -5.95, -26.7572, -50.290497, -8.1437, -26.7572, -50.290497, -8.1437, -26.7572, -44.537098, -5.95, -26.7572, -47.4473, -7.0596, -26.7572, -26.7572, -5.9270997, -47.646698, -50.290497, -8.1437, -26.7572, -26.7572, -7.1011, -51.2768, -26.7572, -7.1011, -51.2768, -50.290497, -8.1437, -26.7572, -26.7572, -8.1437, -54.500298, -53.117798, -8.6939, -26.7572, -26.7572, -8.1437, -54.500298, -50.290497, -8.1437, -26.7572, -55.5951, -9.176, -26.7572, -26.7572, -8.1437, -54.500298, -53.117798, -8.6939, -26.7572, -58.463497, -9.7343, -26.7572, -26.7572, -8.1437, -54.500298, -55.5951, -9.176, -26.7572, -26.7572, -8.1437, -54.500298, -58.463497, -9.7343, -26.7572, -26.7572, -9.7125, -64.0031, -26.7572, -9.7125, -64.0031, -58.463497, -9.7343, -26.7572, -62.057194, -10.4337, -26.7572, -26.7572, -9.7125, -64.0031, -62.057194, -10.4337, -26.7572, -26.7572, -10.4337, -68.371895, -26.7572, -8.1437, -54.500298, -26.7572, -9.7125, -64.0031, -26.7572, -9.111199, -60.361, -26.7572, -8.1437, -54.500298, -26.7572, -9.111199, -60.361, -26.7572, -8.6276, -57.4314, -64.963196, -10.4337, -26.7572, -26.7572, -10.4337, -68.371895, -62.057194, -10.4337, -26.7572, -64.963196, -10.4337, -26.7572, -26.7572, -10.4337, -70.9319, -26.7572, -10.4337, -68.371895, -64.963196, -10.4337, -26.7572, -62.057194, -10.4337, -26.7572, -62.057194, -10.4337, -23.412498, -68.2489, -10.4337, -26.7572, -26.7572, -10.4337, -70.9319, -64.963196, -10.4337, -26.7572, -68.2489, -10.4337, -26.7572, -26.7572, -10.4337, -73.7615, -26.7572, -10.4337, -70.9319, -64.963196, -10.4337, -26.7572, -62.057194, -10.4337, -23.412498, -64.963196, -10.4337, -23.412498, -68.2489, -10.4337, -26.7572, -64.963196, -10.4337, -26.7572, -64.963196, -10.4337, -23.412498, -64.963196, -10.4337, -23.412498, -62.057194, -10.4337, -23.412498, -62.057194, -10.4337, -20.174799, -71.2999, -10.4337, -26.7572, -26.7572, -10.4337, -73.7615, -68.2489, -10.4337, -26.7572, -71.2999, -10.4337, -26.7572, -26.7572, -10.4337, -76.6203, -26.7572, -10.4337, -73.7615, -26.7572, -10.4337, -76.6203, -71.2999, -10.4337, -26.7572, -26.7572, -10.4337, -79.0804, -26.7572, -10.4337, -79.0804, -71.2999, -10.4337, -26.7572, -26.7572, -10.4337, -82.2434, -26.7572, -10.4337, -82.2434, -71.2999, -10.4337, -26.7572, -73.8239, -10.4337, -26.7572, -73.8239, -10.4337, -23.412498, -73.8239, -10.4337, -26.7572, -71.2999, -10.4337, -26.7572, -68.2489, -10.4337, -26.7572, -64.963196, -10.4337, -23.412498, -68.2489, -10.4337, -23.412498, -71.2999, -10.4337, -26.7572, -68.2489, -10.4337, -26.7572, -68.2489, -10.4337, -23.412498, -73.8239, -10.4337, -23.412498, -71.2999, -10.4337, -26.7572, -71.2999, -10.4337, -23.412498, -71.2999, -10.4337, -26.7572, -68.2489, -10.4337, -23.412498, -71.2999, -10.4337, -23.412498, -73.8239, -10.4337, -20.0679, -73.8239, -10.4337, -23.412498, -71.2999, -10.4337, -23.412498, -73.8239, -10.4337, -20.0679, -71.2999, -10.4337, -23.412498, -71.2999, -10.4337, -20.0908, -71.2999, -10.4337, -23.412498, -68.2489, -10.4337, -20.118599, -71.2999, -10.4337, -20.0908, -71.2999, -10.4337, -23.412498, -68.2489, -10.4337, -23.412498, -68.2489, -10.4337, -20.118599, -71.2999, -10.4337, -20.0908, -68.2489, -10.4337, -20.118599, -68.2489, -10.4337, -16.7739, -68.2489, -10.4337, -20.118599, -64.963196, -10.4337, -16.803799, -68.2489, -10.4337, -16.7739, -68.2489, -10.4337, -23.412498, -64.963196, -10.4337, -20.1484, -68.2489, -10.4337, -20.118599, -68.2489, -10.4337, -20.118599, -64.963196, -10.4337, -20.1484, -64.963196, -10.4337, -16.803799, -68.2489, -10.4337, -23.412498, -64.963196, -10.4337, -23.412498, -64.963196, -10.4337, -20.1484, -62.057194, -10.4337, -20.174799, -64.963196, -10.4337, -16.803799, -64.963196, -10.4337, -20.1484, -64.963196, -10.4337, -23.412498, -62.057194, -10.4337, -20.174799, -64.963196, -10.4337, -20.1484, -3.3446002, -1.458, -26.7572, -6.6709, -1.7958, -30.683697, -3.3313003, -1.7853999, -30.628498, -3.3446002, -1.458, -26.7572, -6.6893, -1.458, -26.7572, -6.6709, -1.7958, -30.683697, -6.6893, -3.6573, -40.6287, -6.6689, -4.8491, -44.313698, -6.6243997, -3.6573, -40.6287, -6.6689, -4.8491, -44.313698, -3.3446002, -3.6573, -40.6287, -6.6243997, -3.6573, -40.6287, -6.6893, -3.6573, -40.6287, -9.908199, -3.6573, -40.6287, -6.6689, -4.8491, -44.313698, -6.6689, -4.8491, -44.313698, -3.297, -3.6573, -40.6287, -3.3446002, -3.6573, -40.6287, -6.6689, -4.8491, -44.313698, -9.908199, -3.6573, -40.6287, -9.969999, -4.8431, -44.295097, -10.033899, -3.6573, -40.6287, -9.969999, -4.8431, -44.295097, -9.908199, -3.6573, -40.6287, -9.969999, -4.8431, -44.295097, -10.033899, -3.6573, -40.6287, -13.0655, -3.6573, -40.6287, -9.969999, -4.8431, -44.295097, -13.0655, -3.6573, -40.6287, -13.160999, -4.8373, -44.2772, -13.378601, -3.6573, -40.6287, -13.160999, -4.8373, -44.2772, -13.0655, -3.6573, -40.6287, -13.160999, -4.8373, -44.2772, -13.378601, -3.6573, -40.6287, -16.3079, -3.6573, -40.6287, -13.160999, -4.8373, -44.2772, -16.3079, -3.6573, -40.6287, -16.4166, -4.8314, -44.2589, -16.7232, -3.6573, -40.6287, -16.4166, -4.8314, -44.2589, -16.3079, -3.6573, -40.6287, -16.4166, -4.8314, -44.2589, -16.7232, -3.6573, -40.6287, -19.5913, -3.6573, -40.6287, -16.4166, -4.8314, -44.2589, -19.5913, -3.6573, -40.6287, -19.7153, -4.8254, -44.2403, -20.0679, -3.6573, -40.6287, -19.7153, -4.8254, -44.2403, -19.5913, -3.6573, -40.6287, -19.7153, -4.8254, -44.2403, -20.0679, -3.6573, -40.6287, -22.8746, -3.6573, -40.6287, -19.7153, -4.8254, -44.2403, -22.8746, -3.6573, -40.6287, -23.013899, -4.8194, -44.221798, -23.412498, -3.6573, -40.6287, -23.013899, -4.8194, -44.221798, -22.8746, -3.6573, -40.6287, -23.013899, -4.8194, -44.221798, -23.412498, -3.6573, -40.6287, -26.7572, -3.6573, -40.6287, -23.013899, -4.8194, -44.221798, -26.7572, -3.6573, -40.6287, -26.7572, -4.8125997, -44.200798, -26.7572, -4.8125997, -44.200798, -23.1474, -5.9323997, -47.6632, -23.013899, -4.8194, -44.221798, -26.7572, -4.8125997, -44.200798, -26.7572, -5.9270997, -47.646698, -23.1474, -5.9323997, -47.6632, -23.013899, -4.8194, -44.221798, -19.8335, -5.9372997, -47.6783, -19.7153, -4.8254, -44.2403, -23.013899, -4.8194, -44.221798, -23.1474, -5.9323997, -47.6632, -19.8335, -5.9372997, -47.6783, -19.7153, -4.8254, -44.2403, -19.8335, -5.9372997, -47.6783, -16.5194, -5.9421997, -47.6935, -19.7153, -4.8254, -44.2403, -16.5194, -5.9421997, -47.6935, -16.4166, -4.8314, -44.2589, -26.7572, -5.9270997, -47.646698, -23.2876, -7.1015997, -51.2782, -23.1474, -5.9323997, -47.6632, -26.7572, -5.9270997, -47.646698, -26.7572, -7.1011, -51.2768, -23.2876, -7.1015997, -51.2782, -26.7572, -7.1011, -51.2768, -23.412498, -8.1437, -54.500298, -23.2876, -7.1015997, -51.2782, -26.7572, -7.1011, -51.2768, -26.7572, -8.1437, -54.500298, -23.412498, -8.1437, -54.500298, -23.2876, -7.1015997, -51.2782, -23.412498, -8.1437, -54.500298, -20.0679, -8.1437, -54.500298, -23.1474, -5.9323997, -47.6632, -23.2876, -7.1015997, -51.2782, -19.9572, -7.1019998, -51.2795, -23.2876, -7.1015997, -51.2782, -20.0679, -8.1437, -54.500298, -19.9572, -7.1019998, -51.2795, -23.1474, -5.9323997, -47.6632, -19.9572, -7.1019998, -51.2795, -19.8335, -5.9372997, -47.6783, -19.9572, -7.1019998, -51.2795, -20.0679, -8.1437, -54.500298, -16.7232, -8.1437, -54.500298, -19.8335, -5.9372997, -47.6783, -19.9572, -7.1019998, -51.2795, -16.626799, -7.1024, -51.2808, -19.9572, -7.1019998, -51.2795, -16.7232, -8.1437, -54.500298, -16.626799, -7.1024, -51.2808, -19.8335, -5.9372997, -47.6783, -16.626799, -7.1024, -51.2808, -16.5194, -5.9421997, -47.6935, -16.626799, -7.1024, -51.2808, -16.7232, -8.1437, -54.500298, -13.4286, -8.1437, -54.500298, -16.5194, -5.9421997, -47.6935, -16.626799, -7.1024, -51.2808, -13.344299, -7.1029, -51.282097, -16.626799, -7.1024, -51.2808, -13.4286, -8.1437, -54.500298, -13.344299, -7.1029, -51.282097, -16.5194, -5.9421997, -47.6935, -13.344299, -7.1029, -51.282097, -13.250801, -5.947, -47.708397, -16.4166, -4.8314, -44.2589, -16.5194, -5.9421997, -47.6935, -13.250801, -5.947, -47.708397, -16.4166, -4.8314, -44.2589, -13.250801, -5.947, -47.708397, -13.160999, -4.8373, -44.2772, -13.4286, -8.1437, -54.500298, -10.0877, -7.1032996, -51.283398, -13.344299, -7.1029, -51.282097, -13.250801, -5.947, -47.708397, -13.344299, -7.1029, -51.282097, -10.0877, -7.1032996, -51.283398, -10.0877, -7.1032996, -51.283398, -13.4286, -8.1437, -54.500298, -13.378601, -8.1437, -54.500298, -10.0877, -7.1032996, -51.283398, -13.378601, -8.1437, -54.500298, -10.141901, -8.1437, -54.500298, -13.160999, -4.8373, -44.2772, -13.250801, -5.947, -47.708397, -10.0277, -5.9518, -47.723198, -13.250801, -5.947, -47.708397, -10.0877, -7.1032996, -51.283398, -10.0277, -5.9518, -47.723198, -13.160999, -4.8373, -44.2772, -10.0277, -5.9518, -47.723198, -9.969999, -4.8431, -44.295097, -10.141901, -8.1437, -54.500298, -6.7531, -7.1036997, -51.284798, -10.0877, -7.1032996, -51.283398, -10.0277, -5.9518, -47.723198, -10.0877, -7.1032996, -51.283398, -6.7531, -7.1036997, -51.284798, -6.7531, -7.1036997, -51.284798, -10.141901, -8.1437, -54.500298, -10.033899, -8.1437, -54.500298, -6.7531, -7.1036997, -51.284798, -10.033899, -8.1437, -54.500298, -6.7919, -8.1437, -54.500298, -6.6893, -8.1437, -54.500298, -6.7531, -7.1036997, -51.284798, -6.7919, -8.1437, -54.500298, -6.7531, -7.1036997, -51.284798, -6.6893, -8.1437, -54.500298, -3.4199996, -8.1437, -54.500298, -9.969999, -4.8431, -44.295097, -10.0277, -5.9518, -47.723198, -6.7102, -5.9567, -47.7383, -10.0277, -5.9518, -47.723198, -6.7531, -7.1036997, -51.284798, -6.7102, -5.9567, -47.7383, -9.969999, -4.8431, -44.295097, -6.7102, -5.9567, -47.7383, -6.6689, -4.8491, -44.313698, -6.7531, -7.1036997, -51.284798, -3.4199996, -8.1437, -54.500298, -3.3915, -7.1040998, -51.2861, -3.3915, -7.1040998, -51.2861, -6.7102, -5.9567, -47.7383, -6.7531, -7.1036997, -51.284798, -3.3446002, -8.1437, -54.500298, -3.3915, -7.1040998, -51.2861, -3.4199996, -8.1437, -54.500298, -3.3915, -7.1040998, -51.2861, -3.3446002, -8.1437, -54.500298, -0.048199892, -8.1437, -54.500298, -3.3915, -7.1040998, -51.2861, -3.3602, -5.9617, -47.753597, -6.7102, -5.9567, -47.7383, -3.3602, -5.9617, -47.753597, -6.6689, -4.8491, -44.313698, -6.7102, -5.9567, -47.7383, -3.3915, -7.1040998, -51.2861, -0.048199892, -8.1437, -54.500298, -0.03699994, -7.1046, -51.2874, -0.03699994, -7.1046, -51.2874, -3.3602, -5.9617, -47.753597, -3.3915, -7.1040998, -51.2861, 0, -8.1437, -54.500298, -0.03699994, -7.1046, -51.2874, -0.048199892, -8.1437, -54.500298, -0.03699994, -7.1046, -51.2874, 0, -8.1437, -54.500298, 3.3446, -8.1437, -54.500298, -3.3602, -5.9617, -47.753597, -3.3298001, -4.8552, -44.3325, -6.6689, -4.8491, -44.313698, -3.297, -3.6573, -40.6287, -6.6689, -4.8491, -44.313698, -3.3298001, -4.8552, -44.3325, -0.03699994, -7.1046, -51.2874, -0.024800062, -5.9666, -47.768898, -3.3602, -5.9617, -47.753597, -0.024800062, -5.9666, -47.768898, -3.3298001, -4.8552, -44.3325, -3.3602, -5.9617, -47.753597, -0.012900114, -4.8612, -44.351097, -3.297, -3.6573, -40.6287, -3.3298001, -4.8552, -44.3325, -0.024800062, -5.9666, -47.768898, -0.012900114, -4.8612, -44.351097, -3.3298001, -4.8552, -44.3325, -0.012900114, -4.8612, -44.351097, 0, -3.6573, -40.6287, -3.297, -3.6573, -40.6287, 3.3446, -7.105, -51.2888, -0.024800062, -5.9666, -47.768898, -0.03699994, -7.1046, -51.2874, -0.03699994, -7.1046, -51.2874, 3.3446, -8.1437, -54.500298, 3.3446, -7.105, -51.2888, 3.3446, -5.9716, -47.7842, -0.012900114, -4.8612, -44.351097, -0.024800062, -5.9666, -47.768898, 3.3446, -7.105, -51.2888, 3.3446, -5.9716, -47.7842, -0.024800062, -5.9666, -47.768898, 3.3446, -4.8673, -44.37, 0, -3.6573, -40.6287, -0.012900114, -4.8612, -44.351097, 3.3446, -5.9716, -47.7842, 3.3446, -4.8673, -44.37, -0.012900114, -4.8612, -44.351097, 3.3446, -4.8673, -44.37, 3.3446, -3.6573, -40.6287, 0, -3.6573, -40.6287, 3.3446, -8.1437, -54.500298, 6.6893, -7.1053996, -51.2901, 3.3446, -7.105, -51.2888, 6.6893, -7.1053996, -51.2901, 3.3446, -5.9716, -47.7842, 3.3446, -7.105, -51.2888, 3.3446, -8.1437, -54.500298, 6.6893, -8.1437, -54.500298, 6.6893, -7.1053996, -51.2901, 6.6893, -5.9765, -47.7995, 3.3446, -4.8673, -44.37, 3.3446, -5.9716, -47.7842, 6.6893, -7.1053996, -51.2901, 6.6893, -5.9765, -47.7995, 3.3446, -5.9716, -47.7842, 6.6893, -4.8733997, -44.388798, 3.3446, -3.6573, -40.6287, 3.3446, -4.8673, -44.37, 6.6893, -5.9765, -47.7995, 6.6893, -4.8733997, -44.388798, 3.3446, -4.8673, -44.37, 6.6893, -4.8733997, -44.388798, 6.6893, -3.6573, -40.6287, 3.3446, -3.6573, -40.6287, 6.6893, -8.1437, -54.500298, 10.033899, -7.1059, -51.291397, 6.6893, -7.1053996, -51.2901, 10.033899, -7.1059, -51.291397, 6.6893, -5.9765, -47.7995, 6.6893, -7.1053996, -51.2901, 6.6893, -8.1437, -54.500298, 10.033899, -8.1437, -54.500298, 10.033899, -7.1059, -51.291397, 13.378599, -8.1437, -54.500298, 10.033899, -7.1059, -51.291397, 10.033899, -8.1437, -54.500298, 10.033899, -7.1059, -51.291397, 13.378599, -8.1437, -54.500298, 13.444599, -8.1437, -54.500298, 10.033899, -4.8795, -44.4076, 6.6893, -3.6573, -40.6287, 6.6893, -4.8733997, -44.388798, 10.033899, -5.9814, -47.8148, 6.6893, -4.8733997, -44.388798, 6.6893, -5.9765, -47.7995, 10.033899, -7.1059, -51.291397, 10.033899, -5.9814, -47.8148, 6.6893, -5.9765, -47.7995, 10.033899, -5.9814, -47.8148, 10.033899, -4.8795, -44.4076, 6.6893, -4.8733997, -44.388798, 10.033899, -7.1059, -51.291397, 13.444599, -8.1437, -54.500298, 13.429298, -7.1063, -51.292797, 13.429298, -7.1063, -51.292797, 10.033899, -5.9814, -47.8148, 10.033899, -7.1059, -51.291397, 10.033899, -4.8795, -44.4076, 10.033899, -3.6573, -40.6287, 6.6893, -3.6573, -40.6287, 16.7232, -8.1437, -54.500298, 13.429298, -7.1063, -51.292797, 13.444599, -8.1437, -54.500298, 13.429298, -7.1063, -51.292797, 16.7232, -8.1437, -54.500298, 16.8188, -8.1437, -54.500298, 13.412899, -5.9863997, -47.8302, 10.033899, -4.8795, -44.4076, 10.033899, -5.9814, -47.8148, 13.429298, -7.1063, -51.292797, 13.412899, -5.9863997, -47.8302, 10.033899, -5.9814, -47.8148, 13.3967, -4.8856, -44.4265, 10.033899, -3.6573, -40.6287, 10.033899, -4.8795, -44.4076, 13.412899, -5.9863997, -47.8302, 13.3967, -4.8856, -44.4265, 10.033899, -4.8795, -44.4076, 13.3967, -4.8856, -44.4265, 13.378599, -3.6573, -40.6287, 10.033899, -3.6573, -40.6287, 13.429298, -7.1063, -51.292797, 16.8188, -8.1437, -54.500298, 16.7967, -7.1067, -51.294098, 16.7967, -7.1067, -51.294098, 13.412899, -5.9863997, -47.8302, 13.429298, -7.1063, -51.292797, 20.0679, -8.1437, -54.500298, 16.7967, -7.1067, -51.294098, 16.8188, -8.1437, -54.500298, 16.7967, -7.1067, -51.294098, 20.0679, -8.1437, -54.500298, 20.1975, -8.1437, -54.500298, 16.7967, -7.1067, -51.294098, 20.1975, -8.1437, -54.500298, 20.1675, -7.1071997, -51.295498, 16.7967, -7.1067, -51.294098, 16.772999, -5.9914, -47.8456, 13.412899, -5.9863997, -47.8302, 20.1675, -7.1071997, -51.295498, 16.772999, -5.9914, -47.8456, 16.7967, -7.1067, -51.294098, 16.772999, -5.9914, -47.8456, 13.3967, -4.8856, -44.4265, 13.412899, -5.9863997, -47.8302, 20.1675, -7.1071997, -51.295498, 20.135399, -5.9964, -47.861, 16.772999, -5.9914, -47.8456, 16.7495, -4.8917, -44.445297, 13.378599, -3.6573, -40.6287, 13.3967, -4.8856, -44.4265, 16.772999, -5.9914, -47.8456, 16.7495, -4.8917, -44.445297, 13.3967, -4.8856, -44.4265, 20.135399, -5.9964, -47.861, 16.7495, -4.8917, -44.445297, 16.772999, -5.9914, -47.8456, 16.7495, -4.8917, -44.445297, 16.7232, -3.6573, -40.6287, 13.378599, -3.6573, -40.6287, 20.135399, -5.9964, -47.861, 20.103699, -4.8978, -44.4642, 16.7495, -4.8917, -44.445297, 20.103699, -4.8978, -44.4642, 16.7232, -3.6573, -40.6287, 16.7495, -4.8917, -44.445297, 20.103699, -4.8978, -44.4642, 20.0679, -3.6573, -40.6287, 16.7232, -3.6573, -40.6287, 16.7232, 0, 10.033899, 17.4912, 0.0034999999, 7.4080997, 20.0679, 0, 10.033899, 12.824999, 2.8544, 7.9234, 11.2783985, 2.1174998, 3.3446, 12.705599, 1.0321, 4.0927, 12.824999, 2.8544, 7.9234, 11.173899, 3.3332, 6.6554, 11.2783985, 2.1174998, 3.3446, 10.033899, 3.1929998, 0, 13.249599, 2.9931998, -3.4737, 11.278099, 2.3706, 0, 10.033899, 3.1929998, 0, 10.033899, 3.2212, -2.2307, 13.249599, 2.9931998, -3.4737, 20.613499, 1.0832, -3.1922002, 22.5396, -0.014599999, -3.3446002, 20.5402, -0.0411, -1.7683, 20.613499, 1.0832, -3.1922002, 21.09, -0.0218, -4.9179, 22.5396, -0.014599999, -3.3446002, 15.485399, 1.0792, -1.6708999, 14.217899, 2.8779, -3.3838, 15.6619, 2.2603, -2.7998996, 20.613499, 1.0832, -3.1922002, 15.485399, 1.0792, -1.6708999, 15.6619, 2.2603, -2.7998996, 20.613499, 1.0832, -3.1922002, 20.5402, -0.0411, -1.7683, 15.485399, 1.0792, -1.6708999, 20.5402, -0.0411, -1.7683, 23.412498, 0, 0, 20.2292, 0.030499998, -0.05219996, 23.412498, 0, 0, 20.5402, -0.0411, -1.7683, 22.5396, -0.014599999, -3.3446002, 23.412498, 0, 0, 22.5396, -0.014599999, -3.3446002, 23.412498, 0, -3.3446002, 21.09, -0.0218, -4.9179, 23.412498, 0, -6.6893, 22.5396, -0.014599999, -3.3446002, 21.09, -0.0218, -4.9179, 21.2349, 0, -6.6893, 23.412498, 0, -6.6893, 15.485399, 1.0792, -1.6708999, 20.2292, 0.030499998, -0.05219996, 14.261799, 0.9069, 1.1807, 14.217899, 2.8779, -3.3838, 11.278099, 2.3706, 0, 13.249599, 2.9931998, -3.4737, 11.278099, 2.3706, 0, 14.217899, 2.8779, -3.3838, 15.485399, 1.0792, -1.6708999, 11.278099, 2.3706, 0, 15.485399, 1.0792, -1.6708999, 14.261799, 0.9069, 1.1807, 15.485399, 1.0792, -1.6708999, 20.5402, -0.0411, -1.7683, 20.2292, 0.030499998, -0.05219996, 10.033899, 3.3913999, 4.2546997, 11.2783985, 2.1174998, 3.3446, 11.173899, 3.3332, 6.6554, 10.033899, 3.3913999, 4.2546997, 10.033899, 3.1929998, 3.3446, 11.2783985, 2.1174998, 3.3446, 6.6893, 3.3913999, 3.3446, 10.033899, 3.1929998, 3.3446, 10.033899, 3.3913999, 4.2546997, 10.033899, 3.2212, -2.2307, 10.033899, 3.1929998, 0, 6.6893, 3.3913999, 0, 14.0257, 0.0442, 7.3252997, 12.824999, 2.8544, 7.9234, 12.705599, 1.0321, 4.0927, 14.0257, 0.0442, 7.3252997, 13.175798, 2.4743, 8.042, 12.824999, 2.8544, 7.9234, 26.7582, -0.83089995, 6.6893, 26.7582, -1.7623999, 10.033899, 23.412498, -0.35529998, 10.033899, 23.826399, -8.175799, -54.500298, 26.7572, -7.3550997, -51.2602, 23.7466, -7.1323, -51.2931, 23.826399, -8.175799, -54.500298, 26.7572, -8.403, -54.500298, 26.7572, -7.3550997, -51.2602, 26.7572, -7.3550997, -51.2602, 23.6614, -6.0197997, -47.8736, 23.7466, -7.1323, -51.2931, 26.7572, -7.3550997, -51.2602, 26.7572, -6.2504, -47.8447, 23.6614, -6.0197997, -47.8736, 26.7572, -6.2504, -47.8447, 23.577, -4.916, -44.481, 23.6614, -6.0197997, -47.8736, 26.7572, -6.2504, -47.8447, 26.7572, -5.1514, -44.446598, 23.577, -4.916, -44.481, 26.7572, -5.1514, -44.446598, 23.480999, -3.6625998, -40.6287, 23.577, -4.916, -44.481, 26.7572, -5.1514, -44.446598, 26.7572, -3.9166, -40.6287, 23.480999, -3.6625998, -40.6287, 23.808699, -10.464399, -68.371895, 26.7572, -9.876599, -63.426796, 23.8148, -9.6761, -63.593796, 23.808699, -10.464399, -68.371895, 26.7572, -10.693, -68.371895, 26.7572, -9.876599, -63.426796, 26.7572, -9.876599, -63.426796, 23.819199, -9.111899, -60.1742, 23.8148, -9.6761, -63.593796, 26.7572, -9.876599, -63.426796, 26.7572, -9.312699, -60.011097, 23.819199, -9.111899, -60.1742, 26.7572, -9.312699, -60.011097, 23.8225, -8.6786995, -57.548298, 23.819199, -9.111899, -60.1742, 26.7572, -9.312699, -60.011097, 26.7572, -8.881, -57.3956, 23.8225, -8.6786995, -57.548298, 26.7572, -8.881, -57.3956, 23.826399, -8.175799, -54.500298, 23.8225, -8.6786995, -57.548298, 26.7572, -8.881, -57.3956, 26.7572, -8.403, -54.500298, 23.826399, -8.175799, -54.500298, 23.480999, -3.6625998, -40.6287, 26.7572, -3.2101, -37.2077, 23.4646, -2.985, -37.2919, 23.480999, -3.6625998, -40.6287, 26.7572, -3.9166, -40.6287, 26.7572, -3.2101, -37.2077, 23.412498, -8.1437, -54.500298, 20.1675, -7.1071997, -51.295498, 20.1975, -8.1437, -54.500298, 20.1675, -7.1071997, -51.295498, 23.412498, -8.1437, -54.500298, 23.826399, -8.175799, -54.500298, 20.1675, -7.1071997, -51.295498, 23.826399, -8.175799, -54.500298, 23.7466, -7.1323, -51.2931, 23.412498, -10.4337, -68.371895, 20.112099, -9.6529, -63.642, 20.0679, -10.4337, -68.371895, 20.112099, -9.6529, -63.642, 23.412498, -10.4337, -68.371895, 23.808699, -10.464399, -68.371895, 20.112099, -9.6529, -63.642, 23.808699, -10.464399, -68.371895, 23.8148, -9.6761, -63.593796, 23.412498, -3.6573, -40.6287, 20.0679, -2.9615998, -37.315, 20.0679, -3.6573, -40.6287, 20.0679, -2.9615998, -37.315, 23.412498, -3.6573, -40.6287, 23.480999, -3.6625998, -40.6287, 20.0679, -2.9615998, -37.315, 23.480999, -3.6625998, -40.6287, 23.4646, -2.985, -37.2919, 16.7232, -3.6573, -40.6287, 20.0679, -2.9615998, -37.315, 16.7232, -2.9859998, -37.3232, 16.7232, -3.6573, -40.6287, 20.0679, -3.6573, -40.6287, 20.0679, -2.9615998, -37.315, 13.378599, -3.6573, -40.6287, 16.7232, -2.9859998, -37.3232, 13.378599, -2.9889998, -37.3379, 13.378599, -3.6573, -40.6287, 16.7232, -3.6573, -40.6287, 16.7232, -2.9859998, -37.3232, 16.7232, -2.9859998, -37.3232, 13.378599, -2.3234, -34.0607, 13.378599, -2.9889998, -37.3379, 16.7232, -2.9859998, -37.3232, 16.7232, -2.3191, -34.0398, 13.378599, -2.3234, -34.0607, 16.7232, -2.3191, -34.0398, 13.378599, -1.582, -30.410397, 13.378599, -2.3234, -34.0607, 16.7232, -2.3191, -34.0398, 16.7232, -1.5709, -30.3554, 13.378599, -1.582, -30.410397, 10.033899, -3.6573, -40.6287, 13.378599, -2.9889998, -37.3379, 10.033899, -2.9973998, -37.350998, 10.033899, -3.6573, -40.6287, 13.378599, -3.6573, -40.6287, 13.378599, -2.9889998, -37.3379, 6.6893, -3.6573, -40.6287, 10.033899, -2.9973998, -37.350998, 6.6893, -3.0333998, -37.355698, 6.6893, -3.6573, -40.6287, 10.033899, -3.6573, -40.6287, 10.033899, -2.9973998, -37.350998, 3.3446, -3.6573, -40.6287, 6.6893, -3.0333998, -37.355698, 3.3446, -3.0307, -37.3722, 3.3446, -3.6573, -40.6287, 6.6893, -3.6573, -40.6287, 6.6893, -3.0333998, -37.355698, 0, -3.6573, -40.6287, 3.3446, -3.0307, -37.3722, 0, -3.0496, -37.3821, 0, -3.6573, -40.6287, 3.3446, -3.6573, -40.6287, 3.3446, -3.0307, -37.3722, -3.297, -3.6573, -40.6287, 0, -3.0496, -37.3821, -3.3081002, -3.0523, -37.396698, -3.297, -3.6573, -40.6287, 0, -3.6573, -40.6287, 0, -3.0496, -37.3821, -3.3081002, -3.0523, -37.396698, -3.3446002, -3.6573, -40.6287, -3.297, -3.6573, -40.6287, -3.3081002, -3.0523, -37.396698, -6.6243997, -3.6573, -40.6287, -3.3446002, -3.6573, -40.6287, -6.6243997, -3.6573, -40.6287, -3.3081002, -3.0523, -37.396698, -6.6394, -3.0551, -37.4115, -6.6547, -2.4452, -34.1534, -6.6394, -3.0551, -37.4115, -3.3081002, -3.0523, -37.396698, 0, -3.0496, -37.3821, -3.3193002, -2.4413, -34.1324, -3.3081002, -3.0523, -37.396698, -6.6547, -2.4452, -34.1534, -3.3081002, -3.0523, -37.396698, -3.3193002, -2.4413, -34.1324, -6.6709, -1.7958, -30.683697, -6.6547, -2.4452, -34.1534, -3.3193002, -2.4413, -34.1324, 0, -3.0496, -37.3821, 0, -2.4373999, -34.1115, -3.3193002, -2.4413, -34.1324, -6.6709, -1.7958, -30.683697, -3.3193002, -2.4413, -34.1324, -3.3313003, -1.7853999, -30.628498, 0, -2.4373999, -34.1115, -3.3313003, -1.7853999, -30.628498, -3.3193002, -2.4413, -34.1324, 0, -2.4373999, -34.1115, 0, -1.7751, -30.573498, -3.3313003, -1.7853999, -30.628498, -6.6893, -1.458, -26.7572, -9.9988, -2.089, -30.630497, -6.6709, -1.7958, -30.683697, -6.6893, -1.458, -26.7572, -10.033899, -1.8786, -26.7572, -9.9988, -2.089, -30.630497, -10.033899, -1.8786, -26.7572, -13.297701, -2.9966, -30.341599, -9.9988, -2.089, -30.630497, -10.033899, -1.8786, -26.7572, -13.378601, -2.7663999, -26.7572, -13.297701, -2.9966, -30.341599, -13.378601, -2.7663999, -26.7572, -16.6169, -3.2373998, -30.3083, -13.297701, -2.9966, -30.341599, -13.378601, -2.7663999, -26.7572, -16.7232, -3.0929, -26.7572, -16.6169, -3.2373998, -30.3083, -16.7232, -3.0929, -26.7572, -19.9439, -3.2396998, -30.366299, -16.6169, -3.2373998, -30.3083, -19.9439, -3.2396998, -30.366299, -16.5094, -3.3834999, -33.898598, -16.6169, -3.2373998, -30.3083, -16.7232, -3.0929, -26.7572, -20.0679, -3.0929, -26.7572, -19.9439, -3.2396998, -30.366299, -19.9439, -3.2396998, -30.366299, -19.8217, -3.3844, -33.920498, -16.5094, -3.3834999, -33.898598, -20.0679, -3.0929, -26.7572, -23.2703, -3.2421, -30.424297, -19.9439, -3.2396998, -30.366299, -23.2703, -3.2421, -30.424297, -19.8217, -3.3844, -33.920498, -19.9439, -3.2396998, -30.366299, -20.0679, -3.0929, -26.7572, -23.412498, -3.0929, -26.7572, -23.2703, -3.2421, -30.424297, -19.8217, -3.3844, -33.920498, -16.4072, -3.5224, -37.3144, -16.5094, -3.3834999, -33.898598, -23.412498, -3.0929, -26.7572, -26.7572, -3.2445998, -30.4851, -23.2703, -3.2421, -30.424297, -23.412498, -3.0929, -26.7572, -26.7572, -3.0929, -26.7572, -26.7572, -3.2445998, -30.4851, -26.7572, -3.2445998, -30.4851, -23.1339, -3.3852, -33.942398, -23.2703, -3.2421, -30.424297, -23.2703, -3.2421, -30.424297, -23.1339, -3.3852, -33.942398, -19.8217, -3.3844, -33.920498, -26.7572, -3.2445998, -30.4851, -26.7572, -3.3862, -33.9664, -23.1339, -3.3852, -33.942398, -16.4072, -3.5224, -37.3144, -16.7232, -3.6573, -40.6287, -16.3079, -3.6573, -40.6287, -16.4072, -3.5224, -37.3144, -19.5913, -3.6573, -40.6287, -16.7232, -3.6573, -40.6287, -19.8217, -3.3844, -33.920498, -19.7046, -3.5231, -37.3297, -16.4072, -3.5224, -37.3144, -19.5913, -3.6573, -40.6287, -16.4072, -3.5224, -37.3144, -19.7046, -3.5231, -37.3297, -23.1339, -3.3852, -33.942398, -19.7046, -3.5231, -37.3297, -19.8217, -3.3844, -33.920498, -19.7046, -3.5231, -37.3297, -20.0679, -3.6573, -40.6287, -19.5913, -3.6573, -40.6287, -19.7046, -3.5231, -37.3297, -22.8746, -3.6573, -40.6287, -20.0679, -3.6573, -40.6287, -23.1339, -3.3852, -33.942398, -23.0019, -3.5237, -37.3449, -19.7046, -3.5231, -37.3297, -22.8746, -3.6573, -40.6287, -19.7046, -3.5231, -37.3297, -23.0019, -3.5237, -37.3449, -26.7572, -3.3862, -33.9664, -23.0019, -3.5237, -37.3449, -23.1339, -3.3852, -33.942398, -23.0019, -3.5237, -37.3449, -23.412498, -3.6573, -40.6287, -22.8746, -3.6573, -40.6287, -26.7572, -3.3862, -33.9664, -26.7572, -3.5244, -37.3623, -23.0019, -3.5237, -37.3449, -23.0019, -3.5237, -37.3449, -26.7572, -3.6573, -40.6287, -23.412498, -3.6573, -40.6287, -26.7572, -3.6573, -40.6287, -23.0019, -3.5237, -37.3449, -26.7572, -3.5244, -37.3623, -13.297701, -2.9966, -30.341599, -9.9673, -2.6348999, -34.110798, -9.9988, -2.089, -30.630497, -13.297701, -2.9966, -30.341599, -13.2167, -3.2271, -33.93, -9.9673, -2.6348999, -34.110798, 6.6893, -1.7218, -30.475399, 3.3446, -0.98829997, -26.7572, 3.3446, -1.7156, -30.536999, 6.6893, -1.7218, -30.475399, 6.6893, -1.0129, -26.7572, 3.3446, -0.98829997, -26.7572, 26.7573, -1.7447, -30.111198, 23.412498, -1.1158999, -26.7572, 23.429699, -1.5537, -30.243198, 26.7573, -1.7447, -30.111198, 26.7573, -1.3222, -26.7572, 23.412498, -1.1158999, -26.7572, -9.9988, -2.089, -30.630497, -6.6547, -2.4452, -34.1534, -6.6709, -1.7958, -30.683697, -9.9988, -2.089, -30.630497, -9.9673, -2.6348999, -34.110798, -6.6547, -2.4452, -34.1534, 10.033899, -1.61, -30.458897, 6.6893, -1.0129, -26.7572, 6.6893, -1.7218, -30.475399, 10.033899, -1.61, -30.458897, 10.033899, -0.8648, -26.7572, 6.6893, -1.0129, -26.7572, 3.3446, -1.7156, -30.536999, 0, -1.0608, -26.7572, 0, -1.7751, -30.573498, 3.3446, -1.7156, -30.536999, 3.3446, -0.98829997, -26.7572, 0, -1.0608, -26.7572, 23.429699, -1.5537, -30.243198, 20.0679, -1.1922, -26.7572, 20.0679, -1.4942, -30.325497, 23.429699, -1.5537, -30.243198, 23.412498, -1.1158999, -26.7572, 20.0679, -1.1922, -26.7572, 16.7232, -1.5709, -30.3554, 16.7232, -1.3382, -26.7572, 13.378599, -1.582, -30.410397, 13.378599, -1.582, -30.410397, 10.033899, -0.8648, -26.7572, 10.033899, -1.61, -30.458897, 13.378599, -1.582, -30.410397, 13.378599, -0.8401, -26.7572, 10.033899, -0.8648, -26.7572, 0, -1.7751, -30.573498, -3.3446002, -1.458, -26.7572, -3.3313003, -1.7853999, -30.628498, 0, -1.7751, -30.573498, 0, -1.0608, -26.7572, -3.3446002, -1.458, -26.7572, 20.0679, -1.4942, -30.325497, 16.7232, -1.3382, -26.7572, 16.7232, -1.5709, -30.3554, 20.0679, -1.4942, -30.325497, 20.0679, -1.1922, -26.7572, 16.7232, -1.3382, -26.7572, -16.6169, -3.2373998, -30.3083, -13.2167, -3.2271, -33.93, -13.297701, -2.9966, -30.341599, -16.6169, -3.2373998, -30.3083, -16.5094, -3.3834999, -33.898598, -13.2167, -3.2271, -33.93, 6.6893, -2.4083, -34.0766, 3.3446, -1.7156, -30.536999, 3.3446, -2.4013999, -34.1014, 6.6893, -2.4083, -34.0766, 6.6893, -1.7218, -30.475399, 3.3446, -1.7156, -30.536999, 26.7572, -2.5274, -33.9013, 23.429699, -1.5537, -30.243198, 23.4483, -2.3157, -33.9959, 26.7572, -2.5274, -33.9013, 26.7573, -1.7447, -30.111198, 23.429699, -1.5537, -30.243198, 23.4483, -2.3157, -33.9959, 20.0679, -1.4942, -30.325497, 20.0679, -2.2726, -34.0333, 23.4483, -2.3157, -33.9959, 23.429699, -1.5537, -30.243198, 20.0679, -1.4942, -30.325497, -9.9673, -2.6348999, -34.110798, -6.6394, -3.0551, -37.4115, -6.6547, -2.4452, -34.1534, -9.9673, -2.6348999, -34.110798, -9.9375, -3.1504998, -37.3978, -6.6394, -3.0551, -37.4115, 3.3446, -2.4013999, -34.1014, 0, -1.7751, -30.573498, 0, -2.4373999, -34.1115, 3.3446, -2.4013999, -34.1014, 3.3446, -1.7156, -30.536999, 0, -1.7751, -30.573498, -16.5094, -3.3834999, -33.898598, -13.1401005, -3.445, -37.3228, -13.2167, -3.2271, -33.93, -16.5094, -3.3834999, -33.898598, -16.4072, -3.5224, -37.3144, -13.1401005, -3.445, -37.3228, 10.033899, -2.3385, -34.078, 6.6893, -1.7218, -30.475399, 6.6893, -2.4083, -34.0766, 10.033899, -2.3385, -34.078, 10.033899, -1.61, -30.458897, 6.6893, -1.7218, -30.475399, 13.378599, -2.3234, -34.0607, 10.033899, -1.61, -30.458897, 10.033899, -2.3385, -34.078, 13.378599, -2.3234, -34.0607, 13.378599, -1.582, -30.410397, 10.033899, -1.61, -30.458897, 20.0679, -2.2726, -34.0333, 16.7232, -1.5709, -30.3554, 16.7232, -2.3191, -34.0398, 20.0679, -2.2726, -34.0333, 20.0679, -1.4942, -30.325497, 16.7232, -1.5709, -30.3554, -13.2167, -3.2271, -33.93, -9.9375, -3.1504998, -37.3978, -9.9673, -2.6348999, -34.110798, -13.2167, -3.2271, -33.93, -13.1401005, -3.445, -37.3228, -9.9375, -3.1504998, -37.3978, -16.3079, -3.6573, -40.6287, -13.1401005, -3.445, -37.3228, -16.4072, -3.5224, -37.3144, -13.1401005, -3.445, -37.3228, -16.3079, -3.6573, -40.6287, -13.378601, -3.6573, -40.6287, -13.1401005, -3.445, -37.3228, -13.378601, -3.6573, -40.6287, -13.0655, -3.6573, -40.6287, -13.0655, -3.6573, -40.6287, -9.9375, -3.1504998, -37.3978, -13.1401005, -3.445, -37.3228, -9.9375, -3.1504998, -37.3978, -13.0655, -3.6573, -40.6287, -10.033899, -3.6573, -40.6287, -9.9375, -3.1504998, -37.3978, -10.033899, -3.6573, -40.6287, -9.908199, -3.6573, -40.6287, 6.6893, -3.0333998, -37.355698, 3.3446, -2.4013999, -34.1014, 3.3446, -3.0307, -37.3722, 6.6893, -3.0333998, -37.355698, 6.6893, -2.4083, -34.0766, 3.3446, -2.4013999, -34.1014, 10.033899, -2.9973998, -37.350998, 6.6893, -2.4083, -34.0766, 6.6893, -3.0333998, -37.355698, 10.033899, -2.9973998, -37.350998, 10.033899, -2.3385, -34.078, 6.6893, -2.4083, -34.0766, 3.3446, -3.0307, -37.3722, 0, -2.4373999, -34.1115, 0, -3.0496, -37.3821, 3.3446, -3.0307, -37.3722, 3.3446, -2.4013999, -34.1014, 0, -2.4373999, -34.1115, 23.4646, -2.985, -37.2919, 20.0679, -2.2726, -34.0333, 20.0679, -2.9615998, -37.315, 23.4646, -2.985, -37.2919, 23.4483, -2.3157, -33.9959, 20.0679, -2.2726, -34.0333, 26.7572, -3.2101, -37.2077, 23.4483, -2.3157, -33.9959, 23.4646, -2.985, -37.2919, 26.7572, -3.2101, -37.2077, 26.7572, -2.5274, -33.9013, 23.4483, -2.3157, -33.9959, 13.378599, -2.9889998, -37.3379, 10.033899, -2.3385, -34.078, 10.033899, -2.9973998, -37.350998, 13.378599, -2.9889998, -37.3379, 13.378599, -2.3234, -34.0607, 10.033899, -2.3385, -34.078, 20.0679, -2.9615998, -37.315, 16.7232, -2.3191, -34.0398, 16.7232, -2.9859998, -37.3232, 20.0679, -2.9615998, -37.315, 20.0679, -2.2726, -34.0333, 16.7232, -2.3191, -34.0398, -9.908199, -3.6573, -40.6287, -6.6394, -3.0551, -37.4115, -9.9375, -3.1504998, -37.3978, -6.6394, -3.0551, -37.4115, -9.908199, -3.6573, -40.6287, -6.6893, -3.6573, -40.6287, -6.6394, -3.0551, -37.4115, -6.6893, -3.6573, -40.6287, -6.6243997, -3.6573, -40.6287, 23.480999, -3.6625998, -40.6287, 20.103699, -4.8978, -44.4642, 23.577, -4.916, -44.481, 20.103699, -4.8978, -44.4642, 23.480999, -3.6625998, -40.6287, 23.412498, -3.6573, -40.6287, 20.103699, -4.8978, -44.4642, 23.412498, -3.6573, -40.6287, 20.0679, -3.6573, -40.6287, 23.6614, -6.0197997, -47.8736, 20.103699, -4.8978, -44.4642, 20.135399, -5.9964, -47.861, 23.6614, -6.0197997, -47.8736, 23.577, -4.916, -44.481, 20.103699, -4.8978, -44.4642, 23.7466, -7.1323, -51.2931, 20.135399, -5.9964, -47.861, 20.1675, -7.1071997, -51.295498, 23.7466, -7.1323, -51.2931, 23.6614, -6.0197997, -47.8736, 20.135399, -5.9964, -47.861, 23.826399, -8.175799, -54.500298, 20.1689, -8.6489, -57.5607, 23.8225, -8.6786995, -57.548298, 20.1689, -8.6489, -57.5607, 23.826399, -8.175799, -54.500298, 23.412498, -8.1437, -54.500298, 20.1689, -8.6489, -57.5607, 23.412498, -8.1437, -54.500298, 20.1975, -8.1437, -54.500298, 23.819199, -9.111899, -60.1742, 20.1689, -8.6489, -57.5607, 20.1442, -9.085899, -60.207497, 23.819199, -9.111899, -60.1742, 23.8225, -8.6786995, -57.548298, 20.1689, -8.6489, -57.5607, 23.8148, -9.6761, -63.593796, 20.1442, -9.085899, -60.207497, 20.112099, -9.6529, -63.642, 23.8148, -9.6761, -63.593796, 23.819199, -9.111899, -60.1742, 20.1442, -9.085899, -60.207497, -26.7572, -10.4337, -70.9319, -23.4177, -10.4499, -73.721596, -23.4177, -10.4499, -70.8944, -23.4177, -10.4499, -73.721596, -20.0768, -10.466, -70.856895, -23.4177, -10.4499, -70.8944, -26.7572, -10.4337, -70.9319, -26.7572, -10.4337, -73.7615, -23.4177, -10.4499, -73.721596, -23.4177, -10.4499, -73.721596, -20.0768, -10.466, -73.6818, -20.0768, -10.466, -70.856895, -20.0768, -10.466, -73.6818, -16.734499, -10.4822, -70.8193, -20.0768, -10.466, -70.856895, -26.7572, -10.4337, -73.7615, -23.4177, -10.4499, -76.5937, -23.4177, -10.4499, -73.721596, -23.4177, -10.4499, -76.5937, -20.0768, -10.466, -73.6818, -23.4177, -10.4499, -73.721596, -26.7572, -10.4337, -73.7615, -26.7572, -10.4337, -76.6203, -23.4177, -10.4499, -76.5937, -26.7572, -10.4337, -76.6203, -23.4177, -10.4499, -79.1102, -23.4177, -10.4499, -76.5937, -26.7572, -10.4337, -76.6203, -26.7572, -10.4337, -79.0804, -23.4177, -10.4499, -79.1102, -23.4177, -10.4499, -79.1102, -20.0768, -10.466, -76.567, -23.4177, -10.4499, -76.5937, -23.4177, -10.4499, -76.5937, -20.0768, -10.466, -76.567, -20.0768, -10.466, -73.6818, -23.4177, -10.4499, -79.1102, -20.0768, -10.466, -79.14, -20.0768, -10.466, -76.567, -20.0768, -10.466, -73.6818, -16.734499, -10.4822, -73.6419, -16.734499, -10.4822, -70.8193, -20.0768, -10.466, -76.567, -16.734499, -10.4822, -73.6419, -20.0768, -10.466, -73.6818, -16.734499, -10.4822, -73.6419, -13.3908, -10.4984, -70.7818, -16.734499, -10.4822, -70.8193, -20.0768, -10.466, -79.14, -16.734499, -10.4822, -76.5403, -20.0768, -10.466, -76.567, -20.0768, -10.466, -76.567, -16.734499, -10.4822, -76.5403, -16.734499, -10.4822, -73.6419, -20.0768, -10.466, -79.14, -16.734499, -10.4822, -79.1699, -16.734499, -10.4822, -76.5403, -16.734499, -10.4822, -73.6419, -13.3909, -10.4984, -73.602, -13.3908, -10.4984, -70.7818, -16.734499, -10.4822, -76.5403, -13.3909, -10.4984, -73.602, -16.734499, -10.4822, -73.6419, -13.3909, -10.4984, -73.602, -10.045801, -10.5146, -70.7442, -13.3908, -10.4984, -70.7818, -16.734499, -10.4822, -79.1699, -13.3909, -10.4984, -76.513596, -16.734499, -10.4822, -76.5403, -16.734499, -10.4822, -76.5403, -13.3909, -10.4984, -76.513596, -13.3909, -10.4984, -73.602, -16.734499, -10.4822, -79.1699, -13.3909, -10.4984, -79.1997, -13.3909, -10.4984, -76.513596, -13.3909, -10.4984, -73.602, -10.045801, -10.5146, -73.562195, -10.045801, -10.5146, -70.7442, -13.3909, -10.4984, -76.513596, -10.045801, -10.5146, -73.562195, -13.3909, -10.4984, -73.602, -10.045801, -10.5146, -73.562195, -6.6993003, -10.5309, -70.7066, -10.045801, -10.5146, -70.7442, -13.3909, -10.4984, -79.1997, -10.045801, -10.5146, -76.4869, -13.3909, -10.4984, -76.513596, -13.3909, -10.4984, -76.513596, -10.045801, -10.5146, -76.4869, -10.045801, -10.5146, -73.562195, -13.3909, -10.4984, -79.1997, -10.045801, -10.5146, -79.2295, -10.045801, -10.5146, -76.4869, -10.045801, -10.5146, -73.562195, -6.6993003, -10.5309, -73.5222, -6.6993003, -10.5309, -70.7066, -10.045801, -10.5146, -76.4869, -6.6993003, -10.5309, -73.5222, -10.045801, -10.5146, -73.562195, -6.6993003, -10.5309, -73.5222, -3.3514, -10.5471, -70.669, -6.6993003, -10.5309, -70.7066, -10.045801, -10.5146, -79.2295, -6.6993003, -10.5309, -76.4602, -10.045801, -10.5146, -76.4869, -10.045801, -10.5146, -76.4869, -6.6993003, -10.5309, -76.4602, -6.6993003, -10.5309, -73.5222, -10.045801, -10.5146, -79.2295, -6.6993003, -10.5309, -79.2594, -6.6993003, -10.5309, -76.4602, -6.6993003, -10.5309, -73.5222, -3.3514, -10.5471, -73.4823, -3.3514, -10.5471, -70.669, -6.6993003, -10.5309, -76.4602, -3.3514, -10.5471, -73.4823, -6.6993003, -10.5309, -73.5222, -3.3514, -10.5471, -73.4823, -0.0020999908, -10.5633, -70.6314, -3.3514, -10.5471, -70.669, -6.6993003, -10.5309, -79.2594, -3.3514, -10.5471, -76.433395, -6.6993003, -10.5309, -76.4602, -6.6993003, -10.5309, -76.4602, -3.3514, -10.5471, -76.433395, -3.3514, -10.5471, -73.4823, -6.6993003, -10.5309, -79.2594, -3.3514, -10.5471, -79.2893, -3.3514, -10.5471, -76.433395, -3.3514, -10.5471, -73.4823, -0.0020999908, -10.5633, -73.4424, -0.0020999908, -10.5633, -70.6314, -3.3514, -10.5471, -76.433395, -0.0020999908, -10.5633, -73.4424, -3.3514, -10.5471, -73.4823, -0.0020999908, -10.5633, -73.4424, 3.3486998, -10.579599, -70.5937, -0.0020999908, -10.5633, -70.6314, -3.3514, -10.5471, -79.2893, -0.0020999908, -10.5633, -76.4067, -3.3514, -10.5471, -76.433395, -3.3514, -10.5471, -76.433395, -0.0020999908, -10.5633, -76.4067, -0.0020999908, -10.5633, -73.4424, -3.3514, -10.5471, -79.2893, -0.0020999908, -10.5633, -79.3192, -0.0020999908, -10.5633, -76.4067, -0.0020999908, -10.5633, -73.4424, 3.3486998, -10.579599, -73.4024, 3.3486998, -10.579599, -70.5937, -0.0020999908, -10.5633, -76.4067, 3.3486998, -10.579599, -73.4024, -0.0020999908, -10.5633, -73.4424, 3.3486998, -10.579599, -73.4024, 6.7008, -10.595799, -70.5561, 3.3486998, -10.579599, -70.5937, -0.0020999908, -10.5633, -79.3192, 3.3486998, -10.579599, -76.3799, -0.0020999908, -10.5633, -76.4067, -0.0020999908, -10.5633, -76.4067, 3.3486998, -10.579599, -76.3799, 3.3486998, -10.579599, -73.4024, -0.0020999908, -10.5633, -79.3192, 3.3486998, -10.579599, -79.3491, 3.3486998, -10.579599, -76.3799, 3.3486998, -10.579599, -73.4024, 6.7008, -10.595799, -73.3624, 6.7008, -10.595799, -70.5561, 3.3486998, -10.579599, -76.3799, 6.7008, -10.595799, -73.3624, 3.3486998, -10.579599, -73.4024, 6.7008, -10.595799, -73.3624, 10.054299, -10.6119995, -70.518394, 6.7008, -10.595799, -70.5561, 3.3486998, -10.579599, -79.3491, 6.7008, -10.595799, -76.353195, 3.3486998, -10.579599, -76.3799, 3.3486998, -10.579599, -76.3799, 6.7008, -10.595799, -76.353195, 6.7008, -10.595799, -73.3624, 3.3486998, -10.579599, -79.3491, 6.7008, -10.595799, -79.379, 6.7008, -10.595799, -76.353195, 6.7008, -10.595799, -73.3624, 10.054299, -10.6119995, -73.322395, 10.054299, -10.6119995, -70.518394, 6.7008, -10.595799, -76.353195, 10.054299, -10.6119995, -73.322395, 6.7008, -10.595799, -73.3624, 10.054299, -10.6119995, -73.322395, 13.4093, -10.6283, -70.4807, 10.054299, -10.6119995, -70.518394, 6.7008, -10.595799, -79.379, 10.054299, -10.6119995, -76.3264, 6.7008, -10.595799, -76.353195, 6.7008, -10.595799, -76.353195, 10.054299, -10.6119995, -76.3264, 10.054299, -10.6119995, -73.322395, 6.7008, -10.595799, -79.379, 10.054299, -10.6119995, -79.4089, 10.054299, -10.6119995, -76.3264, 10.054299, -10.6119995, -73.322395, 13.4093, -10.6283, -73.282394, 13.4093, -10.6283, -70.4807, 10.054299, -10.6119995, -76.3264, 13.4093, -10.6283, -73.282394, 10.054299, -10.6119995, -73.322395, 13.4093, -10.6283, -73.282394, 16.7656, -10.6446, -70.443, 13.4093, -10.6283, -70.4807, 10.054299, -10.6119995, -79.4089, 13.4093, -10.6283, -76.2996, 10.054299, -10.6119995, -76.3264, 10.054299, -10.6119995, -76.3264, 13.4093, -10.6283, -76.2996, 13.4093, -10.6283, -73.282394, 10.054299, -10.6119995, -79.4089, 13.4093, -10.6283, -79.4388, 13.4093, -10.6283, -76.2996, 13.4093, -10.6283, -73.282394, 16.7656, -10.6446, -73.2424, 16.7656, -10.6446, -70.443, 13.4093, -10.6283, -76.2996, 16.7656, -10.6446, -73.2424, 13.4093, -10.6283, -73.282394, 16.7656, -10.6446, -73.2424, 20.1234, -10.6608, -70.4053, 16.7656, -10.6446, -70.443, 13.4093, -10.6283, -79.4388, 16.7656, -10.6446, -76.2728, 13.4093, -10.6283, -76.2996, 13.4093, -10.6283, -76.2996, 16.7656, -10.6446, -76.2728, 16.7656, -10.6446, -73.2424, 13.4093, -10.6283, -79.4388, 16.7656, -10.6446, -79.468796, 16.7656, -10.6446, -76.2728, 16.7656, -10.6446, -73.2424, 20.1234, -10.6608, -73.2023, 20.1234, -10.6608, -70.4053, 16.7656, -10.6446, -76.2728, 20.1234, -10.6608, -73.2023, 16.7656, -10.6446, -73.2424, 20.1234, -10.6608, -73.2023, 23.812, -10.6786995, -70.3639, 20.1234, -10.6608, -70.4053, 16.7656, -10.6446, -79.468796, 20.1234, -10.6608, -76.245995, 16.7656, -10.6446, -76.2728, 16.7656, -10.6446, -76.2728, 20.1234, -10.6608, -76.245995, 20.1234, -10.6608, -73.2023, 16.7656, -10.6446, -79.468796, 20.1234, -10.6608, -79.498695, 20.1234, -10.6608, -76.245995, 20.1234, -10.6608, -73.2023, 23.7337, -10.6783, -73.1593, 23.812, -10.6786995, -70.3639, 20.1234, -10.6608, -76.245995, 23.7337, -10.6783, -73.1593, 20.1234, -10.6608, -73.2023, 23.7337, -10.6783, -73.1593, 26.7572, -10.693, -70.330795, 23.812, -10.6786995, -70.3639, 23.7337, -10.6783, -73.1593, 26.7572, -10.693, -73.1232, 26.7572, -10.693, -70.330795, 20.1234, -10.6608, -76.245995, 23.6481, -10.677899, -76.2178, 23.7337, -10.6783, -73.1593, 23.6481, -10.677899, -76.2178, 26.7572, -10.693, -73.1232, 23.7337, -10.6783, -73.1593, 20.1234, -10.6608, -79.498695, 23.6481, -10.677899, -76.2178, 20.1234, -10.6608, -76.245995, 23.6481, -10.677899, -76.2178, 26.7572, -10.693, -76.193, 26.7572, -10.693, -73.1232, 20.1234, -10.6608, -79.498695, 23.555399, -10.6775, -79.529396, 23.6481, -10.677899, -76.2178, 23.555399, -10.6775, -79.529396, 26.7572, -10.693, -76.193, 23.6481, -10.677899, -76.2178, 23.555399, -10.6775, -79.529396, 26.7572, -10.693, -79.5579, 26.7572, -10.693, -76.193, -26.7572, -10.4337, -79.0804, -23.412498, -10.4337, -82.2434, -23.4177, -10.4499, -79.1102, -26.7572, -10.4337, -79.0804, -26.7572, -10.4337, -82.2434, -23.412498, -10.4337, -82.2434, 23.412498, -10.4337, -82.2434, 26.7572, -10.693, -79.5579, 23.555399, -10.6775, -79.529396, 23.412498, -10.4337, -82.2434, 26.7572, -10.693, -82.2434, 26.7572, -10.693, -79.5579, 23.812, -10.6786995, -70.3639, 26.7572, -10.693, -68.371895, 23.808699, -10.464399, -68.371895, 23.812, -10.6786995, -70.3639, 26.7572, -10.693, -70.330795, 26.7572, -10.693, -68.371895, 20.0679, -10.4337, -82.2434, 23.555399, -10.6775, -79.529396, 20.1234, -10.6608, -79.498695, 20.0679, -10.4337, -82.2434, 23.412498, -10.4337, -82.2434, 23.555399, -10.6775, -79.529396, 23.812, -10.6786995, -70.3639, 20.0679, -10.4337, -68.371895, 20.1234, -10.6608, -70.4053, 20.0679, -10.4337, -68.371895, 23.812, -10.6786995, -70.3639, 23.808699, -10.464399, -68.371895, 20.0679, -10.4337, -68.371895, 23.808699, -10.464399, -68.371895, 23.412498, -10.4337, -68.371895, 16.7232, -10.4337, -82.2434, 20.1234, -10.6608, -79.498695, 16.7656, -10.6446, -79.468796, 16.7232, -10.4337, -82.2434, 20.0679, -10.4337, -82.2434, 20.1234, -10.6608, -79.498695, 16.7656, -10.6446, -70.443, 20.0679, -10.4337, -68.371895, 16.7232, -10.4337, -68.371895, 16.7656, -10.6446, -70.443, 20.1234, -10.6608, -70.4053, 20.0679, -10.4337, -68.371895, 13.378599, -10.4337, -82.2434, 16.7656, -10.6446, -79.468796, 13.4093, -10.6283, -79.4388, 13.378599, -10.4337, -82.2434, 16.7232, -10.4337, -82.2434, 16.7656, -10.6446, -79.468796, 13.4093, -10.6283, -70.4807, 16.7232, -10.4337, -68.371895, 13.378599, -10.4337, -68.371895, 13.4093, -10.6283, -70.4807, 16.7656, -10.6446, -70.443, 16.7232, -10.4337, -68.371895, 10.033899, -10.4337, -82.2434, 13.4093, -10.6283, -79.4388, 10.054299, -10.6119995, -79.4089, 10.033899, -10.4337, -82.2434, 13.378599, -10.4337, -82.2434, 13.4093, -10.6283, -79.4388, 10.054299, -10.6119995, -70.518394, 13.378599, -10.4337, -68.371895, 10.033899, -10.4337, -68.371895, 10.054299, -10.6119995, -70.518394, 13.4093, -10.6283, -70.4807, 13.378599, -10.4337, -68.371895, 6.6893, -10.4337, -82.2434, 10.054299, -10.6119995, -79.4089, 6.7008, -10.595799, -79.379, 6.6893, -10.4337, -82.2434, 10.033899, -10.4337, -82.2434, 10.054299, -10.6119995, -79.4089, 6.7008, -10.595799, -70.5561, 10.033899, -10.4337, -68.371895, 6.6893, -10.4337, -68.371895, 6.7008, -10.595799, -70.5561, 10.054299, -10.6119995, -70.518394, 10.033899, -10.4337, -68.371895, 3.3446, -10.4337, -82.2434, 6.7008, -10.595799, -79.379, 3.3486998, -10.579599, -79.3491, 3.3446, -10.4337, -82.2434, 6.6893, -10.4337, -82.2434, 6.7008, -10.595799, -79.379, 3.3486998, -10.579599, -70.5937, 6.6893, -10.4337, -68.371895, 3.3446, -10.4337, -68.371895, 3.3486998, -10.579599, -70.5937, 6.7008, -10.595799, -70.5561, 6.6893, -10.4337, -68.371895, 0, -10.4337, -82.2434, 3.3486998, -10.579599, -79.3491, -0.0020999908, -10.5633, -79.3192, 0, -10.4337, -82.2434, 3.3446, -10.4337, -82.2434, 3.3486998, -10.579599, -79.3491, -0.0020999908, -10.5633, -70.6314, 3.3446, -10.4337, -68.371895, 0, -10.4337, -68.371895, -0.0020999908, -10.5633, -70.6314, 3.3486998, -10.579599, -70.5937, 3.3446, -10.4337, -68.371895, -3.3446002, -10.4337, -82.2434, -0.0020999908, -10.5633, -79.3192, -3.3514, -10.5471, -79.2893, -3.3446002, -10.4337, -82.2434, 0, -10.4337, -82.2434, -0.0020999908, -10.5633, -79.3192, -3.3514, -10.5471, -70.669, -6.6893, -10.4337, -68.371895, -6.6993003, -10.5309, -70.7066, -3.3514, -10.5471, -70.669, -3.3446002, -10.4337, -68.371895, -6.6893, -10.4337, -68.371895, -6.6893, -10.4337, -68.371895, -10.045801, -10.5146, -70.7442, -6.6993003, -10.5309, -70.7066, -6.6893, -10.4337, -68.371895, -10.033899, -10.4337, -68.371895, -10.045801, -10.5146, -70.7442, -6.6893, -10.4337, -82.2434, -3.3514, -10.5471, -79.2893, -6.6993003, -10.5309, -79.2594, -6.6893, -10.4337, -82.2434, -3.3446002, -10.4337, -82.2434, -3.3514, -10.5471, -79.2893, -10.033899, -10.4337, -82.2434, -6.6993003, -10.5309, -79.2594, -10.045801, -10.5146, -79.2295, -10.033899, -10.4337, -82.2434, -6.6893, -10.4337, -82.2434, -6.6993003, -10.5309, -79.2594, -10.033899, -10.4337, -68.371895, -13.3908, -10.4984, -70.7818, -10.045801, -10.5146, -70.7442, -10.033899, -10.4337, -68.371895, -13.378601, -10.4337, -68.371895, -13.3908, -10.4984, -70.7818, -13.378601, -10.4337, -82.2434, -10.045801, -10.5146, -79.2295, -13.3909, -10.4984, -79.1997, -13.378601, -10.4337, -82.2434, -10.033899, -10.4337, -82.2434, -10.045801, -10.5146, -79.2295, -13.378601, -10.4337, -68.371895, -16.734499, -10.4822, -70.8193, -13.3908, -10.4984, -70.7818, -13.378601, -10.4337, -68.371895, -16.7232, -10.4337, -68.371895, -16.734499, -10.4822, -70.8193, -16.7232, -10.4337, -68.371895, -20.0768, -10.466, -70.856895, -16.734499, -10.4822, -70.8193, -16.7232, -10.4337, -68.371895, -20.0679, -10.4337, -68.371895, -20.0768, -10.466, -70.856895, -16.7232, -10.4337, -82.2434, -13.3909, -10.4984, -79.1997, -16.734499, -10.4822, -79.1699, -16.7232, -10.4337, -82.2434, -13.378601, -10.4337, -82.2434, -13.3909, -10.4984, -79.1997, -20.0679, -10.4337, -68.371895, -23.4177, -10.4499, -70.8944, -20.0768, -10.466, -70.856895, -20.0679, -10.4337, -68.371895, -23.412498, -10.4337, -68.371895, -23.4177, -10.4499, -70.8944, -20.0679, -10.4337, -82.2434, -16.734499, -10.4822, -79.1699, -20.0768, -10.466, -79.14, -20.0679, -10.4337, -82.2434, -16.7232, -10.4337, -82.2434, -16.734499, -10.4822, -79.1699, -23.412498, -10.4337, -68.371895, -26.7572, -10.4337, -70.9319, -23.4177, -10.4499, -70.8944, -23.412498, -10.4337, -68.371895, -26.7572, -10.4337, -68.371895, -26.7572, -10.4337, -70.9319, -23.412498, -10.4337, -82.2434, -20.0768, -10.466, -79.14, -23.4177, -10.4499, -79.1102, -23.412498, -10.4337, -82.2434, -20.0679, -10.4337, -82.2434, -20.0768, -10.466, -79.14, 13.378599, -1.0746, 20.0679, 13.378599, -2.2719998, 23.3902, 10.033899, -0.501, 23.412498, 20.0679, -1.0746, 13.378601, 16.7232, -1.0746, 16.7232, 16.7232, -1.0746, 13.378601, 16.7232, -1.0746, 16.7232, 15.0508995, -1.0746, 20.0679, 13.378599, -1.0746, 20.0679, 16.7232, -1.0746, 16.7232, 16.7232, -1.8294, 20.0679, 15.0508995, -1.0746, 20.0679, 23.412498, -0.35529998, 10.033899, 23.412498, -1.8294, 13.378601, 20.0679, -1.0746, 13.378601, 10.501598, 0, 20.5087, 6.6893, 0, 20.0679, 10.080299, 0, 20.0679, 10.501598, 0, 20.5087, 6.6893, -0.2814, 23.412498, 6.6893, 0, 20.0679, -20.0679, 0, 13.378601, -16.7232, 0, 13.868401, -16.7232, -0.26209998, 16.7232, -20.0679, 0, 13.378601, -17.213, 0, 13.378601, -16.7232, 0, 13.868401, -16.7232, -0.26209998, 16.7232, -13.378601, 0, 16.7232, -13.378601, 0, 20.0679, -24.056599, 0, 10.033899, -25.9681, -0.2875, 13.378601, -26.0077, -0.2465, 10.033899, -25.9681, -0.2875, 13.378601, -24.056599, 0, 10.033899, -23.412498, 0, 10.6779995, -25.9681, -0.2875, 13.378601, -23.412498, 0, 10.6779995, -23.412498, 0, 13.378601, -23.8078, 0, 6.6893, -26.0077, -0.2465, 10.033899, -25.945799, -0.25779998, 6.6893, -26.0077, -0.2465, 10.033899, -23.8078, 0, 6.6893, -23.489, 0, 9.386499, -26.0077, -0.2465, 10.033899, -23.489, 0, 9.386499, -24.056599, 0, 10.033899, -25.4212, -0.5907, 16.7232, -23.412498, -0.692, 20.0679, -25.174, -0.7391, 20.0679, -25.479, -0.4588, 0.143, -23.412498, 0, 3.3446, -25.869999, -0.2764, 3.3446, -25.479, -0.4588, 0.143, -23.412498, -0.2913, 0.26119995, -23.412498, 0, 3.3446, -25.869999, -0.2764, 3.3446, -23.8078, 0, 6.6893, -25.945799, -0.25779998, 6.6893, -25.869999, -0.2764, 3.3446, -23.412498, 0, 3.3446, -23.8078, 0, 6.6893, -17.2383, -0.66719997, -19.6364, -20.0679, -0.43429998, -16.7232, -20.0679, -1.7572, -20.0679, -20.0679, -0.43429998, -16.7232, -20.0679, 0, -13.378599, -23.412498, 0, -13.378599, -9.318001, -0.3969, -19.413, -6.6893, -0.9271, -20.0679, -6.6893, 0, -16.7232, -6.6893, 0, -16.7232, -3.3446002, -0.9271, -16.7232, -3.3446002, 0, -13.7127, 16.7232, 0, -20.0679, 20.0679, -0.2837, -20.0679, 20.0679, 0, -16.7232, 13.378599, 0, -23.412498, 16.7232, -0.3105, -23.412498, 16.7232, 0, -20.0679, 20.0679, 0, -16.7232, 23.412498, -0.456, -16.7232, 23.412498, 0, -13.378599, 23.412498, 0, -6.6893, 23.412498, 0, -3.3446002, 22.5396, -0.014599999, -3.3446002, 15.0508995, -0.40039998, 16.7232, 13.378599, 0, 16.7232, 15.0508995, -0.6288, 13.378601, 14.439199, 0, 13.6437, 15.0508995, 0, 10.033899, 15.0508995, -0.6288, 13.378601, 15.0508995, 0, 10.033899, 14.439199, 0, 13.6437, 13.8046, 0, 13.2202, 15.0508995, 0, 10.033899, 13.8046, 0, 13.2202, 13.8046, 0, 10.033899, 3.0140998, 0, -16.7232, 3.3446, -0.3989, -12.9443, 0, 0, -13.378599, -23.412498, -0.692, 20.0679, -25.4212, -0.5907, 16.7232, -23.412498, -0.36479998, 16.7232, -23.412498, -0.692, 20.0679, -20.0679, -0.36479998, 20.0679, -20.0679, -0.5589, 23.412498, -16.7232, -0.36479998, 23.412498, -13.378601, -0.2814, 23.412498, -13.378601, -1.1209999, 26.7572, -20.0679, -0.36479998, 16.7232, -16.7232, -0.26209998, 16.7232, -16.7232, -0.36479998, 20.0679, -16.7232, -0.36479998, 20.0679, -13.378601, 0, 20.0679, -13.378601, -0.2814, 23.412498, -23.412498, -0.7373, 23.412498, -20.0679, -0.5589, 23.412498, -20.0679, -1.1374999, 26.7572, -25.174, -0.7391, 20.0679, -23.412498, -0.692, 20.0679, -23.412498, -0.7373, 23.412498, -20.0679, -0.5589, 23.412498, -16.7232, -0.36479998, 23.412498, -16.7232, -1.1374999, 26.7572, 26.7582, -1.7623999, 10.033899, 26.7582, -1.8294, 13.378601, 23.412498, -1.8294, 13.378601, 13.378599, 0, 16.7232, 14.439199, 0, 13.6437, 15.0508995, -0.6288, 13.378601, 13.378599, 0, 16.7232, 13.700699, 0, 14.602699, 14.439199, 0, 13.6437, 15.0508995, -0.6288, 13.378601, 15.0508995, 0, 10.033899, 16.7232, 0, 10.033899, 15.0508995, -0.40039998, 16.7232, 16.7232, -1.0746, 13.378601, 16.7232, -1.0746, 16.7232, 13.378599, 0, 16.7232, 13.378599, -1.0746, 20.0679, 11.0191, 0, 20.0679, 15.0508995, -0.40039998, 16.7232, 13.378599, -1.0746, 20.0679, 13.378599, 0, 16.7232, 20.0679, -0.2837, -20.0679, 23.412498, -1.0924, -20.0679, 23.412498, -0.456, -16.7232, 26.572199, -0.95549995, -13.659599, 23.412498, 0, -13.378599, 23.412498, -0.456, -16.7232, 13.378599, -0.8401, -26.7572, 16.7232, -1.3382, -26.7572, 16.7232, -0.3105, -23.412498, 16.7232, -0.3105, -23.412498, 20.0679, -0.8222, -23.412498, 20.0679, -0.2837, -20.0679, 16.7232, -1.3382, -26.7572, 13.378599, -0.8401, -26.7572, 13.378599, -1.582, -30.410397, 16.7232, -1.3382, -26.7572, 20.0679, -1.1922, -26.7572, 20.0679, -0.8222, -23.412498, 26.7577, -0.8804, -16.7232, 23.412498, -0.456, -16.7232, 23.412498, -1.0924, -20.0679, 20.0679, -0.8222, -23.412498, 23.412498, -1.0924, -23.412498, 23.412498, -1.0924, -20.0679, 3.3446, -0.3434, -23.412498, 0, -0.9271, -20.0679, 0, -0.9271, -23.412498, -10.033899, -0.9271, -23.412498, -6.6893, -1.3241999, -23.412498, -6.6893, -0.9271, -20.0679, 2.3184, 0, -20.4362, 0, -0.9271, -16.7232, 0, -0.9271, -20.0679, 39.877598, -3.6573, 23.412498, 36.078598, -3.0746999, 26.7572, 36.2121, -2.9357, 23.412498, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 26.7572, 36.078598, -3.0746999, 26.7572, 52.997097, -8.1437, 23.412498, 49.5743, -6.9732, 26.7572, 49.5645, -6.9698997, 23.412498, 52.997097, -8.1437, 23.412498, 52.997097, -8.1437, 26.7572, 49.5743, -6.9732, 26.7572, 52.997097, -8.1437, 20.0679, 52.997097, -8.1437, 23.412498, 49.5645, -6.9698997, 23.412498, 49.5743, -6.9732, 26.7572, 46.556797, -5.9413, 23.412498, 49.5645, -6.9698997, 23.412498, 49.5743, -6.9732, 26.7572, 46.577698, -5.9484997, 26.7572, 46.556797, -5.9413, 23.412498, 52.997097, -8.1437, 20.0679, 49.5645, -6.9698997, 23.412498, 49.5547, -6.9665, 20.0679, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 20.0679, 49.5547, -6.9665, 20.0679, 52.997097, -8.1437, 16.7232, 49.5547, -6.9665, 20.0679, 49.5449, -6.9631996, 16.7232, 49.5547, -6.9665, 20.0679, 46.5149, -5.927, 16.7232, 49.5449, -6.9631996, 16.7232, 49.5645, -6.9698997, 23.412498, 46.535797, -5.9342, 20.0679, 49.5547, -6.9665, 20.0679, 49.5547, -6.9665, 20.0679, 46.535797, -5.9342, 20.0679, 46.5149, -5.927, 16.7232, 49.5645, -6.9698997, 23.412498, 46.556797, -5.9413, 23.412498, 46.535797, -5.9342, 20.0679, 46.535797, -5.9342, 20.0679, 43.1641, -4.7812, 16.7232, 46.5149, -5.927, 16.7232, 46.535797, -5.9342, 20.0679, 43.1877, -4.7892, 20.0679, 43.1641, -4.7812, 16.7232, 46.556797, -5.9413, 23.412498, 43.1877, -4.7892, 20.0679, 46.535797, -5.9342, 20.0679, 43.1877, -4.7892, 20.0679, 39.877598, -3.6573, 16.7232, 43.1641, -4.7812, 16.7232, 43.1877, -4.7892, 20.0679, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, 16.7232, 46.556797, -5.9413, 23.412498, 43.2113, -4.7973, 23.412498, 43.1877, -4.7892, 20.0679, 43.2113, -4.7973, 23.412498, 39.877598, -3.6573, 20.0679, 43.1877, -4.7892, 20.0679, 46.577698, -5.9484997, 26.7572, 43.2113, -4.7973, 23.412498, 46.556797, -5.9413, 23.412498, 43.2113, -4.7973, 23.412498, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 20.0679, 46.577698, -5.9484997, 26.7572, 43.234898, -4.8054, 26.7572, 43.2113, -4.7973, 23.412498, 43.234898, -4.8054, 26.7572, 39.877598, -3.6573, 23.412498, 43.2113, -4.7973, 23.412498, 43.234898, -4.8054, 26.7572, 39.877598, -3.6573, 26.7572, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 20.0679, 36.2121, -2.9357, 23.412498, 36.2075, -2.9348, 20.0679, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, 23.412498, 36.2121, -2.9357, 23.412498, 39.877598, -3.6573, 16.7232, 39.877598, -3.6573, 20.0679, 36.2075, -2.9348, 20.0679, 39.877598, -3.6573, 16.7232, 36.2075, -2.9348, 20.0679, 36.2028, -2.9338999, 16.7232, 39.877598, -3.7507, 13.378601, 36.2028, -2.9338999, 16.7232, 36.1422, -2.9887998, 13.378601, 39.877598, -3.7507, 13.378601, 39.877598, -3.6573, 16.7232, 36.2028, -2.9338999, 16.7232, 52.997097, -8.1437, 13.378601, 49.5449, -6.9631996, 16.7232, 49.520397, -6.9795, 13.378601, 52.997097, -8.1437, 13.378601, 52.997097, -8.1437, 16.7232, 49.5449, -6.9631996, 16.7232, 39.877598, -3.7507, 10.033899, 36.1422, -2.9887998, 13.378601, 36.153297, -2.972, 10.033899, 39.877598, -3.7507, 10.033899, 39.877598, -3.7507, 13.378601, 36.1422, -2.9887998, 13.378601, 52.997097, -8.1437, 10.033899, 49.520397, -6.9795, 13.378601, 49.510597, -6.9763, 10.033899, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 13.378601, 49.520397, -6.9795, 13.378601, 52.997097, -8.1437, 6.6893, 52.997097, -8.1437, 10.033899, 49.510597, -6.9763, 10.033899, 49.520397, -6.9795, 13.378601, 46.4435, -5.9493, 10.033899, 49.510597, -6.9763, 10.033899, 49.520397, -6.9795, 13.378601, 46.4646, -5.9563, 13.378601, 46.4435, -5.9493, 10.033899, 52.997097, -8.1437, 6.6893, 49.510597, -6.9763, 10.033899, 49.500698, -6.973, 6.6893, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 6.6893, 49.500698, -6.973, 6.6893, 49.510597, -6.9763, 10.033899, 46.4435, -5.9493, 10.033899, 46.4225, -5.9421997, 6.6893, 49.510597, -6.9763, 10.033899, 46.4225, -5.9421997, 6.6893, 49.500698, -6.973, 6.6893, 46.4646, -5.9563, 13.378601, 43.0695, -4.8195, 10.033899, 46.4435, -5.9493, 10.033899, 46.4646, -5.9563, 13.378601, 43.0932, -4.8275, 13.378601, 43.0695, -4.8195, 10.033899, 43.0932, -4.8275, 13.378601, 39.877598, -3.7507, 10.033899, 43.0695, -4.8195, 10.033899, 43.0932, -4.8275, 13.378601, 39.877598, -3.7507, 13.378601, 39.877598, -3.7507, 10.033899, 43.0695, -4.8195, 10.033899, 39.877598, -3.7507, 10.033899, 39.877598, -3.7507, 6.6893, 46.4435, -5.9493, 10.033899, 43.0695, -4.8195, 10.033899, 43.0458, -4.8115997, 6.6893, 43.0695, -4.8195, 10.033899, 39.877598, -3.7507, 6.6893, 43.0458, -4.8115997, 6.6893, 46.4435, -5.9493, 10.033899, 43.0458, -4.8115997, 6.6893, 46.4225, -5.9421997, 6.6893, 43.0458, -4.8115997, 6.6893, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 3.3446, 43.0458, -4.8115997, 6.6893, 39.877598, -3.7507, 3.3446, 43.0221, -4.8036, 3.3446, 46.4225, -5.9421997, 6.6893, 43.0458, -4.8115997, 6.6893, 43.0221, -4.8036, 3.3446, 43.0221, -4.8036, 3.3446, 39.877598, -3.7507, 3.3446, 39.877598, -3.7507, 0, 46.4225, -5.9421997, 6.6893, 43.0221, -4.8036, 3.3446, 46.4015, -5.9351997, 3.3446, 49.500698, -6.973, 6.6893, 46.4225, -5.9421997, 6.6893, 46.4015, -5.9351997, 3.3446, 43.0221, -4.8036, 3.3446, 39.877598, -3.7507, 0, 42.998398, -4.7957, 0, 46.4015, -5.9351997, 3.3446, 43.0221, -4.8036, 3.3446, 42.998398, -4.7957, 0, 42.998398, -4.7957, 0, 39.877598, -3.7507, 0, 39.877598, -3.7507, -3.3446002, 49.500698, -6.973, 6.6893, 46.4015, -5.9351997, 3.3446, 49.4909, -6.9697, 3.3446, 52.997097, -8.1437, 3.3446, 49.500698, -6.973, 6.6893, 49.4909, -6.9697, 3.3446, 52.997097, -8.1437, 0, 52.997097, -8.1437, 3.3446, 49.4909, -6.9697, 3.3446, 49.4909, -6.9697, 3.3446, 46.4015, -5.9351997, 3.3446, 46.380398, -5.9282, 0, 46.4015, -5.9351997, 3.3446, 42.998398, -4.7957, 0, 46.380398, -5.9282, 0, 52.997097, -8.1437, 0, 49.4909, -6.9697, 3.3446, 49.4811, -6.9663997, 0, 49.4909, -6.9697, 3.3446, 46.380398, -5.9282, 0, 49.4811, -6.9663997, 0, 52.997097, -8.1437, -3.3446002, 52.997097, -8.1437, 0, 49.4811, -6.9663997, 0, 42.998398, -4.7957, 0, 39.877598, -3.7507, -3.3446002, 42.974697, -4.7878, -3.3446002, 46.380398, -5.9282, 0, 42.998398, -4.7957, 0, 42.974697, -4.7878, -3.3446002, 42.974697, -4.7878, -3.3446002, 39.877598, -3.7507, -3.3446002, 39.877598, -3.7507, -6.6893, 42.974697, -4.7878, -3.3446002, 39.877598, -3.7507, -6.6893, 42.951, -4.7798, -6.6893, 46.380398, -5.9282, 0, 42.974697, -4.7878, -3.3446002, 46.359398, -5.9210997, -3.3446002, 46.359398, -5.9210997, -3.3446002, 42.974697, -4.7878, -3.3446002, 42.951, -4.7798, -6.6893, 49.4811, -6.9663997, 0, 46.380398, -5.9282, 0, 46.359398, -5.9210997, -3.3446002, 46.359398, -5.9210997, -3.3446002, 42.951, -4.7798, -6.6893, 46.338398, -5.9140997, -6.6893, 52.997097, -8.1437, -3.3446002, 49.4811, -6.9663997, 0, 49.4712, -6.9631, -3.3446002, 49.4811, -6.9663997, 0, 46.359398, -5.9210997, -3.3446002, 49.4712, -6.9631, -3.3446002, 49.4712, -6.9631, -3.3446002, 46.359398, -5.9210997, -3.3446002, 46.338398, -5.9140997, -6.6893, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, -3.3446002, 49.4712, -6.9631, -3.3446002, 49.4712, -6.9631, -3.3446002, 46.338398, -5.9140997, -6.6893, 49.4614, -6.9598, -6.6893, 52.997097, -8.1437, -6.6893, 49.4712, -6.9631, -3.3446002, 49.4614, -6.9598, -6.6893, 39.877598, -3.7507, 6.6893, 36.153297, -2.972, 10.033899, 36.1898, -2.9299998, 6.6893, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 10.033899, 36.153297, -2.972, 10.033899, 39.877598, -3.7507, 3.3446, 36.1898, -2.9299998, 6.6893, 36.2081, -2.9062, 3.3446, 39.877598, -3.7507, 3.3446, 39.877598, -3.7507, 6.6893, 36.1898, -2.9299998, 6.6893, 39.877598, -3.7507, 0, 36.2081, -2.9062, 3.3446, 36.211597, -2.8971, 0, 39.877598, -3.7507, 0, 39.877598, -3.7507, 3.3446, 36.2081, -2.9062, 3.3446, 39.877598, -3.7507, -3.3446002, 36.211597, -2.8971, 0, 36.2533, -2.8497999, -3.3446002, 39.877598, -3.7507, -3.3446002, 39.877598, -3.7507, 0, 36.211597, -2.8971, 0, 39.877598, -3.7507, -6.6893, 36.2533, -2.8497999, -3.3446002, 36.2803, -2.8172998, -6.6893, 39.877598, -3.7507, -6.6893, 39.877598, -3.7507, -3.3446002, 36.2533, -2.8497999, -3.3446002, 52.997097, -8.1437, -10.033899, 49.4614, -6.9598, -6.6893, 49.4666, -6.9364, -10.033899, 52.997097, -8.1437, -10.033899, 52.997097, -8.1437, -6.6893, 49.4614, -6.9598, -6.6893, 39.877598, -3.6573, -10.033899, 36.2803, -2.8172998, -6.6893, 36.3083, -2.7835999, -10.109299, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, -6.6893, 36.2803, -2.8172998, -6.6893, 39.877598, -3.6573, -13.378599, 36.3083, -2.7835999, -10.109299, 36.1797, -2.9064, -13.456699, 39.877598, -3.6573, -13.378599, 39.877598, -3.6573, -10.033899, 36.3083, -2.7835999, -10.109299, 39.877598, -3.6573, -16.7232, 36.1797, -2.9064, -13.456699, 36.2015, -2.8792, -16.7232, 39.877598, -3.6573, -16.7232, 39.877598, -3.6573, -13.378599, 36.1797, -2.9064, -13.456699, 39.877598, -3.6573, -20.0679, 36.2015, -2.8792, -16.7232, 36.1471, -2.928, -20.0679, 39.877598, -3.6573, -20.0679, 39.877598, -3.6573, -16.7232, 36.2015, -2.8792, -16.7232, 39.877598, -3.6573, -23.412498, 42.8416, -4.6944, -26.7572, 42.881, -4.6844, -23.412498, 39.877598, -3.6573, -23.412498, 39.877598, -3.6808999, -26.7572, 42.8416, -4.6944, -26.7572, 42.8416, -4.6944, -26.7572, 46.2636, -5.8410997, -23.412498, 42.881, -4.6844, -23.412498, 42.8416, -4.6944, -26.7572, 46.2278, -5.8524, -26.7572, 46.2636, -5.8410997, -23.412498, 46.2278, -5.8524, -26.7572, 49.427498, -6.923, -23.412498, 46.2636, -5.8410997, -23.412498, 46.2278, -5.8524, -26.7572, 49.4036, -6.9384, -26.7572, 49.427498, -6.923, -23.412498, 49.4036, -6.9384, -26.7572, 52.997097, -8.1437, -23.412498, 49.427498, -6.923, -23.412498, 49.4036, -6.9384, -26.7572, 52.997097, -8.1672, -26.7572, 52.997097, -8.1437, -23.412498, 52.997097, -8.1437, -23.412498, 56.4827, -8.7755995, -26.7572, 56.4571, -8.7476, -23.412498, 52.997097, -8.1437, -23.412498, 52.997097, -8.1672, -26.7572, 56.4827, -8.7755995, -26.7572, 56.4827, -8.7755995, -26.7572, 59.414497, -9.2638, -23.412498, 56.4571, -8.7476, -23.412498, 56.4827, -8.7755995, -26.7572, 59.4335, -9.2907, -26.7572, 59.414497, -9.2638, -23.412498, 59.4335, -9.2907, -26.7572, 62.3756, -9.7807, -23.412498, 59.414497, -9.2638, -23.412498, 59.4335, -9.2907, -26.7572, 62.356697, -9.8008995, -26.7572, 62.3756, -9.7807, -23.412498, 62.356697, -9.8008995, -26.7572, 66.1166, -10.4337, -23.412498, 62.3756, -9.7807, -23.412498, 62.356697, -9.8008995, -26.7572, 66.1166, -10.4572, -26.7572, 66.1166, -10.4337, -23.412498, 39.877598, -3.6573, -23.412498, 36.1471, -2.928, -20.0679, 36.1424, -2.9269998, -23.412498, 39.877598, -3.6573, -23.412498, 39.877598, -3.6573, -20.0679, 36.1471, -2.928, -20.0679, 36.1471, -2.928, -20.0679, 33.0948, -2.3312, -23.412498, 36.1424, -2.9269998, -23.412498, 36.1471, -2.928, -20.0679, 33.1148, -2.3351, -20.0679, 33.0948, -2.3312, -23.412498, 33.1148, -2.3351, -20.0679, 29.564798, -1.6410999, -23.412498, 33.0948, -2.3312, -23.412498, 33.1148, -2.3351, -20.0679, 29.562698, -1.6407, -20.0679, 29.564798, -1.6410999, -23.412498, 29.562698, -1.6407, -20.0679, 26.7582, -1.0924, -23.412498, 29.564798, -1.6410999, -23.412498, 29.562698, -1.6407, -20.0679, 26.7582, -1.0924, -20.0679, 26.7582, -1.0924, -23.412498, 29.6302, -1.5764999, -13.594999, 26.7577, -0.8804, -16.7232, 29.7123, -1.5057, -16.7232, 29.6302, -1.5764999, -13.594999, 26.572199, -0.95549995, -13.659599, 26.7577, -0.8804, -16.7232, 30.0488, -1.2003, -6.6893, 26.572199, -0.4003, -10.314899, 30.0059, -1.2408999, -10.242399, 30.0488, -1.2003, -6.6893, 26.7577, -0.3464, -6.6893, 26.572199, -0.4003, -10.314899, 29.536499, -2.2303, 13.378601, 26.7582, -1.7623999, 10.033899, 29.586699, -2.188, 10.033899, 29.536499, -2.2303, 13.378601, 26.7582, -1.8294, 13.378601, 26.7582, -1.7623999, 10.033899, 29.8112, -1.4067, 0, 26.7577, -0.4894, -3.3446002, 29.9525, -1.2836, -3.3446002, 29.8112, -1.4067, 0, 26.7577, -0.6956, 0, 26.7577, -0.4894, -3.3446002, 29.550499, -2.2133, 20.0679, 26.7582, -1.8294, 16.7232, 29.552599, -2.2137, 16.7232, 29.550499, -2.2133, 20.0679, 26.7582, -1.8294, 20.0679, 26.7582, -1.8294, 16.7232, 29.3928, -1.796, -26.7572, 33.0948, -2.3312, -23.412498, 29.564798, -1.6410999, -23.412498, 29.3928, -1.796, -26.7572, 32.9589, -2.4371, -26.7572, 33.0948, -2.3312, -23.412498, 29.7136, -1.4886999, 6.6893, 26.7577, -0.73109996, 3.3446, 29.7848, -1.4277999, 3.3446, 29.7136, -1.4886999, 6.6893, 26.7582, -0.83089995, 6.6893, 26.7577, -0.73109996, 3.3446, 29.1145, -2.5956, 26.7572, 26.7582, -2.2719998, 23.3902, 29.548399, -2.2129, 23.412498, 29.1145, -2.5956, 26.7572, 26.7582, -2.7837, 26.6955, 26.7582, -2.2719998, 23.3902, 29.7123, -1.5057, -16.7232, 26.7582, -1.0924, -20.0679, 29.562698, -1.6407, -20.0679, 29.7123, -1.5057, -16.7232, 26.7577, -0.8804, -16.7232, 26.7582, -1.0924, -20.0679, 30.0059, -1.2408999, -10.242399, 26.572199, -0.95549995, -13.659599, 29.6302, -1.5764999, -13.594999, 30.0059, -1.2408999, -10.242399, 26.572199, -0.4003, -10.314899, 26.572199, -0.95549995, -13.659599, 29.586699, -2.188, 10.033899, 26.7582, -0.83089995, 6.6893, 29.7136, -1.4886999, 6.6893, 29.586699, -2.188, 10.033899, 26.7582, -1.7623999, 10.033899, 26.7582, -0.83089995, 6.6893, 29.9525, -1.2836, -3.3446002, 26.7577, -0.3464, -6.6893, 30.0488, -1.2003, -6.6893, 29.9525, -1.2836, -3.3446002, 26.7577, -0.4894, -3.3446002, 26.7577, -0.3464, -6.6893, 29.552599, -2.2137, 16.7232, 26.7582, -1.8294, 13.378601, 29.536499, -2.2303, 13.378601, 29.552599, -2.2137, 16.7232, 26.7582, -1.8294, 16.7232, 26.7582, -1.8294, 13.378601, 29.7848, -1.4277999, 3.3446, 26.7577, -0.6956, 0, 29.8112, -1.4067, 0, 29.7848, -1.4277999, 3.3446, 26.7577, -0.73109996, 3.3446, 26.7577, -0.6956, 0, 29.548399, -2.2129, 23.412498, 26.7582, -1.8294, 20.0679, 29.550499, -2.2133, 20.0679, 29.548399, -2.2129, 23.412498, 26.7582, -2.2719998, 23.3902, 26.7582, -1.8294, 20.0679, 33.1982, -2.3009999, -13.5196, 29.7123, -1.5057, -16.7232, 33.2289, -2.25, -16.7232, 33.1982, -2.3009999, -13.5196, 29.6302, -1.5764999, -13.594999, 29.7123, -1.5057, -16.7232, 33.4719, -2.0886, -6.6893, 30.0059, -1.2408999, -10.242399, 33.4514, -2.0843, -10.169699, 33.4719, -2.0886, -6.6893, 30.0488, -1.2003, -6.6893, 30.0059, -1.2408999, -10.242399, 33.2822, -2.9943, 13.378601, 29.586699, -2.188, 10.033899, 33.2915, -2.9626, 10.033899, 33.2822, -2.9943, 13.378601, 29.536499, -2.2303, 13.378601, 29.586699, -2.188, 10.033899, 33.3661, -2.2345, 0, 29.9525, -1.2836, -3.3446002, 33.4326, -2.1487, -3.3446002, 33.3661, -2.2345, 0, 29.8112, -1.4067, 0, 29.9525, -1.2836, -3.3446002, 33.3628, -2.9636998, 20.0679, 29.552599, -2.2137, 16.7232, 33.3428, -2.9598, 16.7232, 33.3628, -2.9636998, 20.0679, 29.550499, -2.2133, 20.0679, 29.552599, -2.2137, 16.7232, 33.3828, -2.9677, 23.412498, 29.550499, -2.2133, 20.0679, 33.3628, -2.9636998, 20.0679, 33.3828, -2.9677, 23.412498, 29.548399, -2.2129, 23.412498, 29.550499, -2.2133, 20.0679, 33.347797, -2.2975, 6.6893, 29.7848, -1.4277999, 3.3446, 33.370598, -2.2531, 3.3446, 33.347797, -2.2975, 6.6893, 29.7136, -1.4886999, 6.6893, 29.7848, -1.4277999, 3.3446, 33.146698, -3.214, 26.7572, 29.548399, -2.2129, 23.412498, 33.3828, -2.9677, 23.412498, 33.146698, -3.214, 26.7572, 29.1145, -2.5956, 26.7572, 29.548399, -2.2129, 23.412498, 33.2289, -2.25, -16.7232, 29.562698, -1.6407, -20.0679, 33.1148, -2.3351, -20.0679, 33.2289, -2.25, -16.7232, 29.7123, -1.5057, -16.7232, 29.562698, -1.6407, -20.0679, 32.9589, -2.4371, -26.7572, 36.1424, -2.9269998, -23.412498, 33.0948, -2.3312, -23.412498, 32.9589, -2.4371, -26.7572, 36.0678, -2.9959998, -26.7572, 36.1424, -2.9269998, -23.412498, 33.4514, -2.0843, -10.169699, 29.6302, -1.5764999, -13.594999, 33.1982, -2.3009999, -13.5196, 33.4514, -2.0843, -10.169699, 30.0059, -1.2408999, -10.242399, 29.6302, -1.5764999, -13.594999, 33.2915, -2.9626, 10.033899, 29.7136, -1.4886999, 6.6893, 33.347797, -2.2975, 6.6893, 33.2915, -2.9626, 10.033899, 29.586699, -2.188, 10.033899, 29.7136, -1.4886999, 6.6893, 33.4326, -2.1487, -3.3446002, 30.0488, -1.2003, -6.6893, 33.4719, -2.0886, -6.6893, 33.4326, -2.1487, -3.3446002, 29.9525, -1.2836, -3.3446002, 30.0488, -1.2003, -6.6893, 33.3428, -2.9598, 16.7232, 29.536499, -2.2303, 13.378601, 33.2822, -2.9943, 13.378601, 33.3428, -2.9598, 16.7232, 29.552599, -2.2137, 16.7232, 29.536499, -2.2303, 13.378601, 33.370598, -2.2531, 3.3446, 29.8112, -1.4067, 0, 33.3661, -2.2345, 0, 33.370598, -2.2531, 3.3446, 29.7848, -1.4277999, 3.3446, 29.8112, -1.4067, 0, 36.1797, -2.9064, -13.456699, 33.2289, -2.25, -16.7232, 36.2015, -2.8792, -16.7232, 36.1797, -2.9064, -13.456699, 33.1982, -2.3009999, -13.5196, 33.2289, -2.25, -16.7232, 36.2803, -2.8172998, -6.6893, 33.4514, -2.0843, -10.169699, 36.3083, -2.7835999, -10.109299, 36.2803, -2.8172998, -6.6893, 33.4719, -2.0886, -6.6893, 33.4514, -2.0843, -10.169699, 36.1422, -2.9887998, 13.378601, 33.2915, -2.9626, 10.033899, 36.153297, -2.972, 10.033899, 36.1422, -2.9887998, 13.378601, 33.2822, -2.9943, 13.378601, 33.2915, -2.9626, 10.033899, 36.211597, -2.8971, 0, 33.4326, -2.1487, -3.3446002, 36.2533, -2.8497999, -3.3446002, 36.211597, -2.8971, 0, 33.3661, -2.2345, 0, 33.4326, -2.1487, -3.3446002, 36.2075, -2.9348, 20.0679, 33.3428, -2.9598, 16.7232, 36.2028, -2.9338999, 16.7232, 36.2075, -2.9348, 20.0679, 33.3628, -2.9636998, 20.0679, 33.3428, -2.9598, 16.7232, 36.153297, -2.972, 10.033899, 33.347797, -2.2975, 6.6893, 36.1898, -2.9299998, 6.6893, 36.153297, -2.972, 10.033899, 33.2915, -2.9626, 10.033899, 33.347797, -2.2975, 6.6893, 36.1898, -2.9299998, 6.6893, 33.370598, -2.2531, 3.3446, 36.2081, -2.9062, 3.3446, 36.1898, -2.9299998, 6.6893, 33.347797, -2.2975, 6.6893, 33.370598, -2.2531, 3.3446, 36.078598, -3.0746999, 26.7572, 33.3828, -2.9677, 23.412498, 36.2121, -2.9357, 23.412498, 36.078598, -3.0746999, 26.7572, 33.146698, -3.214, 26.7572, 33.3828, -2.9677, 23.412498, 36.2015, -2.8792, -16.7232, 33.1148, -2.3351, -20.0679, 36.1471, -2.928, -20.0679, 36.2015, -2.8792, -16.7232, 33.2289, -2.25, -16.7232, 33.1148, -2.3351, -20.0679, 36.3083, -2.7835999, -10.109299, 33.1982, -2.3009999, -13.5196, 36.1797, -2.9064, -13.456699, 36.3083, -2.7835999, -10.109299, 33.4514, -2.0843, -10.169699, 33.1982, -2.3009999, -13.5196, 36.0678, -2.9959998, -26.7572, 39.877598, -3.6573, -23.412498, 36.1424, -2.9269998, -23.412498, 36.0678, -2.9959998, -26.7572, 39.877598, -3.6808999, -26.7572, 39.877598, -3.6573, -23.412498, 36.2533, -2.8497999, -3.3446002, 33.4719, -2.0886, -6.6893, 36.2803, -2.8172998, -6.6893, 36.2533, -2.8497999, -3.3446002, 33.4326, -2.1487, -3.3446002, 33.4719, -2.0886, -6.6893, 36.2028, -2.9338999, 16.7232, 33.2822, -2.9943, 13.378601, 36.1422, -2.9887998, 13.378601, 36.2028, -2.9338999, 16.7232, 33.3428, -2.9598, 16.7232, 33.2822, -2.9943, 13.378601, 36.2081, -2.9062, 3.3446, 33.3661, -2.2345, 0, 36.211597, -2.8971, 0, 36.2081, -2.9062, 3.3446, 33.370598, -2.2531, 3.3446, 33.3661, -2.2345, 0, 36.2121, -2.9357, 23.412498, 33.3628, -2.9636998, 20.0679, 36.2075, -2.9348, 20.0679, 36.2121, -2.9357, 23.412498, 33.3828, -2.9677, 23.412498, 33.3628, -2.9636998, 20.0679, 42.951, -4.7798, -6.6893, 39.877598, -3.6573, -10.033899, 42.9754, -4.7166, -10.033899, 42.951, -4.7798, -6.6893, 39.877598, -3.7507, -6.6893, 39.877598, -3.6573, -10.033899, 43.1641, -4.7812, 16.7232, 39.877598, -3.7507, 13.378601, 43.0932, -4.8275, 13.378601, 43.1641, -4.7812, 16.7232, 39.877598, -3.6573, 16.7232, 39.877598, -3.7507, 13.378601, 46.338398, -5.9140997, -6.6893, 42.9754, -4.7166, -10.033899, 46.3474, -5.8697, -10.033899, 46.338398, -5.9140997, -6.6893, 42.951, -4.7798, -6.6893, 42.9754, -4.7166, -10.033899, 46.5149, -5.927, 16.7232, 43.0932, -4.8275, 13.378601, 46.4646, -5.9563, 13.378601, 46.5149, -5.927, 16.7232, 43.1641, -4.7812, 16.7232, 43.0932, -4.8275, 13.378601, 49.4614, -6.9598, -6.6893, 46.3474, -5.8697, -10.033899, 49.4666, -6.9364, -10.033899, 49.4614, -6.9598, -6.6893, 46.338398, -5.9140997, -6.6893, 46.3474, -5.8697, -10.033899, 49.5449, -6.9631996, 16.7232, 46.4646, -5.9563, 13.378601, 49.520397, -6.9795, 13.378601, 49.5449, -6.9631996, 16.7232, 46.5149, -5.927, 16.7232, 46.4646, -5.9563, 13.378601, -23.412498, -3.8518999, 39.456898, -26.7572, -3.2777, 36.6322, -23.412498, -3.2477999, 36.6305, -23.412498, -3.8518999, 39.456898, -26.7572, -3.8518999, 39.456898, -26.7572, -3.2777, 36.6322, -16.7232, -3.8518999, 39.456898, -13.378601, -3.243, 36.6252, -13.378601, -3.8518999, 39.456898, -16.7232, -3.8518999, 39.456898, -16.7232, -3.247, 36.6269, -13.378601, -3.243, 36.6252, -13.378601, -1.1209999, 26.7572, -10.033899, -1.8925999, 30.345499, -13.378601, -1.8938999, 30.3512, -10.033899, -1.8925999, 30.345499, -13.378601, -2.619, 33.7235, -13.378601, -1.8938999, 30.3512, -13.378601, -1.1209999, 26.7572, -10.033899, -1.1209999, 26.7572, -10.033899, -1.8925999, 30.345499, -10.033899, -1.8925999, 30.345499, -10.033899, -2.6181998, 33.7199, -13.378601, -2.619, 33.7235, -10.033899, -2.6181998, 33.7199, -13.378601, -3.243, 36.6252, -13.378601, -2.619, 33.7235, -10.033899, -1.1209999, 26.7572, -6.6893, -1.8914, 30.3399, -10.033899, -1.8925999, 30.345499, -6.6893, -1.8914, 30.3399, -10.033899, -2.6181998, 33.7199, -10.033899, -1.8925999, 30.345499, -10.033899, -1.1209999, 26.7572, -6.6893, -1.1209999, 26.7572, -6.6893, -1.8914, 30.3399, -10.033899, -2.6181998, 33.7199, -10.033899, -3.2426, 36.623398, -13.378601, -3.243, 36.6252, -10.033899, -3.2426, 36.623398, -13.378601, -3.8518999, 39.456898, -13.378601, -3.243, 36.6252, -10.033899, -3.2426, 36.623398, -10.033899, -3.8518999, 39.456898, -13.378601, -3.8518999, 39.456898, -6.6893, -2.6174998, 33.7163, -10.033899, -3.2426, 36.623398, -10.033899, -2.6181998, 33.7199, -6.6893, -1.8914, 30.3399, -6.6893, -2.6174998, 33.7163, -10.033899, -2.6181998, 33.7199, -6.6893, -3.2422, 36.6217, -10.033899, -3.8518999, 39.456898, -10.033899, -3.2426, 36.623398, -6.6893, -2.6174998, 33.7163, -6.6893, -3.2422, 36.6217, -10.033899, -3.2426, 36.623398, -6.6893, -3.2422, 36.6217, -6.6893, -3.8518999, 39.456898, -10.033899, -3.8518999, 39.456898, -3.3446002, -1.8901999, 30.3342, -6.6893, -2.6174998, 33.7163, -6.6893, -1.8914, 30.3399, -6.6893, -1.1209999, 26.7572, -3.3446002, -1.8901999, 30.3342, -6.6893, -1.8914, 30.3399, -6.6893, -1.1209999, 26.7572, -3.3446002, -1.1209999, 26.7572, -3.3446002, -1.8901999, 30.3342, -3.3446002, -1.8901999, 30.3342, -3.3446002, -2.6167, 33.7128, -6.6893, -2.6174998, 33.7163, -3.3446002, -2.6167, 33.7128, -6.6893, -3.2422, 36.6217, -6.6893, -2.6174998, 33.7163, -3.3446002, -1.1209999, 26.7572, 0, -1.8889999, 30.3286, -3.3446002, -1.8901999, 30.3342, 0, -1.8889999, 30.3286, -3.3446002, -2.6167, 33.7128, -3.3446002, -1.8901999, 30.3342, -3.3446002, -1.1209999, 26.7572, 0, -1.1209999, 26.7572, 0, -1.8889999, 30.3286, -3.3446002, -3.2417998, 36.6199, -6.6893, -3.8518999, 39.456898, -6.6893, -3.2422, 36.6217, -3.3446002, -2.6167, 33.7128, -3.3446002, -3.2417998, 36.6199, -6.6893, -3.2422, 36.6217, -3.3446002, -3.2417998, 36.6199, -3.3446002, -3.8518999, 39.456898, -6.6893, -3.8518999, 39.456898, 0, -1.8889999, 30.3286, 0, -2.616, 33.709198, -3.3446002, -2.6167, 33.7128, 0, -2.616, 33.709198, -3.3446002, -3.2417998, 36.6199, -3.3446002, -2.6167, 33.7128, 0, -1.1209999, 26.7572, 3.3446, -1.8878, 30.323, 0, -1.8889999, 30.3286, 3.3446, -1.8878, 30.323, 0, -2.616, 33.709198, 0, -1.8889999, 30.3286, 0, -1.1209999, 26.7572, 3.3446, -1.1209999, 26.7572, 3.3446, -1.8878, 30.323, 0, -3.2415, 36.6182, -3.3446002, -3.8518999, 39.456898, -3.3446002, -3.2417998, 36.6199, 0, -2.616, 33.709198, 0, -3.2415, 36.6182, -3.3446002, -3.2417998, 36.6199, 0, -3.2415, 36.6182, 0, -3.8518999, 39.456898, -3.3446002, -3.8518999, 39.456898, 3.3446, -1.8878, 30.323, 3.3446, -2.6152, 33.7057, 0, -2.616, 33.709198, 3.3446, -2.6152, 33.7057, 0, -3.2415, 36.6182, 0, -2.616, 33.709198, 3.3446, -1.1209999, 26.7572, 6.6893, -1.8865999, 30.317299, 3.3446, -1.8878, 30.323, 6.6893, -1.8865999, 30.317299, 3.3446, -2.6152, 33.7057, 3.3446, -1.8878, 30.323, 3.3446, -1.1209999, 26.7572, 6.6893, -1.1209999, 26.7572, 6.6893, -1.8865999, 30.317299, 6.6893, -1.1209999, 26.7572, 10.033899, -1.8853999, 30.311699, 6.6893, -1.8865999, 30.317299, 6.6893, -1.1209999, 26.7572, 10.033899, -1.1209999, 26.7572, 10.033899, -1.8853999, 30.311699, 10.033899, -1.8853999, 30.311699, 6.6893, -2.6144, 33.7021, 6.6893, -1.8865999, 30.317299, 6.6893, -1.8865999, 30.317299, 6.6893, -2.6144, 33.7021, 3.3446, -2.6152, 33.7057, 10.033899, -1.8853999, 30.311699, 10.033899, -2.6137, 33.6986, 6.6893, -2.6144, 33.7021, 3.3446, -2.6152, 33.7057, 3.3446, -3.2410998, 36.616398, 0, -3.2415, 36.6182, 3.3446, -3.2410998, 36.616398, 0, -3.8518999, 39.456898, 0, -3.2415, 36.6182, 6.6893, -2.6144, 33.7021, 3.3446, -3.2410998, 36.616398, 3.3446, -2.6152, 33.7057, 3.3446, -3.2410998, 36.616398, 3.3446, -3.8518999, 39.456898, 0, -3.8518999, 39.456898, 10.033899, -2.6137, 33.6986, 6.6893, -3.2407, 36.6147, 6.6893, -2.6144, 33.7021, 6.6893, -2.6144, 33.7021, 6.6893, -3.2407, 36.6147, 3.3446, -3.2410998, 36.616398, 6.6893, -3.2407, 36.6147, 3.3446, -3.8518999, 39.456898, 3.3446, -3.2410998, 36.616398, 10.033899, -2.6137, 33.6986, 10.033899, -3.2403, 36.6129, 6.6893, -3.2407, 36.6147, 6.6893, -3.2407, 36.6147, 6.6893, -3.8518999, 39.456898, 3.3446, -3.8518999, 39.456898, 10.033899, -3.2403, 36.6129, 6.6893, -3.8518999, 39.456898, 6.6893, -3.2407, 36.6147, 10.033899, -3.2403, 36.6129, 10.033899, -3.8518999, 39.456898, 6.6893, -3.8518999, 39.456898, 10.033899, -1.1209999, 26.7572, 13.378599, -2.6734, 30.306, 10.033899, -1.8853999, 30.311699, 10.033899, -1.1209999, 26.7572, 13.378599, -2.2194998, 26.781, 13.378599, -2.6734, 30.306, 13.378599, -2.2194998, 26.781, 16.7232, -2.6727, 30.3004, 13.378599, -2.6734, 30.306, 16.7232, -2.6727, 30.3004, 13.378599, -3.1099, 33.695, 13.378599, -2.6734, 30.306, 13.378599, -2.2194998, 26.781, 16.7232, -2.2194998, 26.781, 16.7232, -2.6727, 30.3004, 16.7232, -2.6727, 30.3004, 16.7232, -3.1094, 33.691498, 13.378599, -3.1099, 33.695, 16.7232, -3.1094, 33.691498, 13.378599, -3.4854, 36.6112, 13.378599, -3.1099, 33.695, 16.7232, -2.2194998, 26.781, 20.0679, -2.672, 30.294699, 16.7232, -2.6727, 30.3004, 20.0679, -2.672, 30.294699, 16.7232, -3.1094, 33.691498, 16.7232, -2.6727, 30.3004, 16.7232, -2.2194998, 26.781, 20.0679, -2.2194998, 26.781, 20.0679, -2.672, 30.294699, 20.0679, -2.2194998, 26.781, 23.412498, -2.6713, 30.289099, 20.0679, -2.672, 30.294699, 20.0679, -2.2194998, 26.781, 23.412498, -2.2194998, 26.781, 23.412498, -2.6713, 30.289099, 23.412498, -2.6713, 30.289099, 20.0679, -3.109, 33.6879, 20.0679, -2.672, 30.294699, 20.0679, -2.672, 30.294699, 20.0679, -3.109, 33.6879, 16.7232, -3.1094, 33.691498, 23.412498, -2.6713, 30.289099, 23.412498, -3.1085, 33.6844, 20.0679, -3.109, 33.6879, 16.7232, -3.1094, 33.691498, 16.7232, -3.4852, 36.609398, 13.378599, -3.4854, 36.6112, 20.0679, -3.109, 33.6879, 16.7232, -3.4852, 36.609398, 16.7232, -3.1094, 33.691498, 16.7232, -3.4852, 36.609398, 13.378599, -3.8518999, 39.456898, 13.378599, -3.4854, 36.6112, 16.7232, -3.4852, 36.609398, 16.7232, -3.8518999, 39.456898, 13.378599, -3.8518999, 39.456898, 20.0679, -3.109, 33.6879, 20.0679, -3.485, 36.607597, 16.7232, -3.4852, 36.609398, 20.0679, -3.485, 36.607597, 16.7232, -3.8518999, 39.456898, 16.7232, -3.4852, 36.609398, 23.412498, -3.1085, 33.6844, 20.0679, -3.485, 36.607597, 20.0679, -3.109, 33.6879, 20.0679, -3.485, 36.607597, 20.0679, -3.8518999, 39.456898, 16.7232, -3.8518999, 39.456898, 23.412498, -3.1085, 33.6844, 23.412498, -3.4847, 36.6059, 20.0679, -3.485, 36.607597, 23.412498, -3.4847, 36.6059, 20.0679, -3.8518999, 39.456898, 20.0679, -3.485, 36.607597, 23.412498, -3.4847, 36.6059, 23.412498, -3.8518999, 39.456898, 20.0679, -3.8518999, 39.456898, 23.412498, -2.2194998, 26.781, 26.7582, -3.0839999, 30.2834, 23.412498, -2.6713, 30.289099, 23.412498, -2.2194998, 26.781, 26.7582, -2.7837, 26.6955, 26.7582, -3.0839999, 30.2834, -26.7572, -3.2777, 36.6322, -23.412498, -2.6287, 33.7341, -23.412498, -3.2477999, 36.6305, -26.7572, -3.2777, 36.6322, -26.7572, -2.6892, 33.737698, -23.412498, -2.6287, 33.7341, -16.7232, -3.247, 36.6269, -13.378601, -2.619, 33.7235, -13.378601, -3.243, 36.6252, -16.7232, -3.247, 36.6269, -16.7232, -2.6272, 33.726997, -13.378601, -2.619, 33.7235, 26.7582, -3.6130998, 36.6041, 23.412498, -3.8518999, 39.456898, 23.412498, -3.4847, 36.6059, 26.7582, -3.6130998, 36.6041, 26.7582, -3.8518999, 39.456898, 23.412498, -3.8518999, 39.456898, 13.378599, -3.4854, 36.6112, 10.033899, -3.8518999, 39.456898, 10.033899, -3.2403, 36.6129, 13.378599, -3.4854, 36.6112, 13.378599, -3.8518999, 39.456898, 10.033899, -3.8518999, 39.456898, 26.7582, -3.3683999, 33.680798, 23.412498, -3.4847, 36.6059, 23.412498, -3.1085, 33.6844, 26.7582, -3.3683999, 33.680798, 26.7582, -3.6130998, 36.6041, 23.412498, -3.4847, 36.6059, -26.7572, -2.6892, 33.737698, -23.412498, -1.9093, 30.3681, -23.412498, -2.6287, 33.7341, -26.7572, -2.6892, 33.737698, -26.7572, -2.0054, 30.3737, -23.412498, -1.9093, 30.3681, -16.7232, -2.6272, 33.726997, -13.378601, -1.8938999, 30.3512, -13.378601, -2.619, 33.7235, -16.7232, -2.6272, 33.726997, -16.7232, -1.9068999, 30.3568, -13.378601, -1.8938999, 30.3512, 13.378599, -3.1099, 33.695, 10.033899, -3.2403, 36.6129, 10.033899, -2.6137, 33.6986, 13.378599, -3.1099, 33.695, 13.378599, -3.4854, 36.6112, 10.033899, -3.2403, 36.6129, -26.7572, -2.0054, 30.3737, -23.412498, -1.1374999, 26.7572, -23.412498, -1.9093, 30.3681, -26.7572, -2.0054, 30.3737, -26.7572, -1.2701, 26.7572, -23.412498, -1.1374999, 26.7572, 26.7582, -3.0839999, 30.2834, 23.412498, -3.1085, 33.6844, 23.412498, -2.6713, 30.289099, 26.7582, -3.0839999, 30.2834, 26.7582, -3.3683999, 33.680798, 23.412498, -3.1085, 33.6844, 13.378599, -2.6734, 30.306, 10.033899, -2.6137, 33.6986, 10.033899, -1.8853999, 30.311699, 13.378599, -2.6734, 30.306, 13.378599, -3.1099, 33.695, 10.033899, -2.6137, 33.6986, -16.7232, -1.9068999, 30.3568, -13.378601, -1.1209999, 26.7572, -13.378601, -1.8938999, 30.3512, -16.7232, -1.9068999, 30.3568, -16.7232, -1.1374999, 26.7572, -13.378601, -1.1209999, 26.7572, -38.5239, -3.6573, -23.412498, -36.0882, -3.5405, -26.7572, -36.0928, -3.4731998, -23.412498, -38.5239, -3.6573, -23.412498, -38.5239, -3.6573, -26.7572, -36.0882, -3.5405, -26.7572, -38.5239, -3.6573, -20.3888, -36.0928, -3.4731998, -23.412498, -36.0969, -3.2475998, -20.410799, -38.5239, -3.6573, -20.3888, -38.5239, -3.6573, -23.412498, -36.0928, -3.4731998, -23.412498, -38.5239, -3.6573, -20.0679, -36.101498, -3.2484, -17.0662, -38.5239, -3.6573, -17.0441, -36.101498, -3.2484, -17.0662, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -20.3888, -36.101498, -3.2484, -17.0662, -38.5239, -3.6573, -20.3888, -36.0969, -3.2475998, -20.410799, -36.0969, -3.2475998, -20.410799, -33.3005, -2.7756, -17.0916, -36.101498, -3.2484, -17.0662, -36.0969, -3.2475998, -20.410799, -33.2906, -2.7739, -20.4364, -33.3005, -2.7756, -17.0916, -33.2906, -2.7739, -20.4364, -30.219301, -2.2554998, -17.1196, -33.3005, -2.7756, -17.0916, -33.2906, -2.7739, -20.4364, -30.2036, -2.2528, -20.4644, -30.219301, -2.2554998, -17.1196, -30.2036, -2.2528, -20.4644, -26.7572, -1.6710999, -17.1511, -30.219301, -2.2554998, -17.1196, -30.2036, -2.2528, -20.4644, -26.7572, -1.6710999, -20.4958, -26.7572, -1.6710999, -17.1511, -38.5239, -3.6573, -16.7232, -36.106598, -2.8727, -13.378599, -38.5239, -3.6573, -13.378599, -36.106598, -2.8727, -13.378599, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -17.0441, -36.106598, -2.8727, -13.378599, -38.5239, -3.6573, -17.0441, -36.101498, -3.2484, -17.0662, -38.5239, -3.6573, -10.033899, -36.106598, -2.8727, -13.378599, -36.1112, -2.8741999, -10.033899, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -13.378599, -36.106598, -2.8727, -13.378599, -38.5239, -3.6573, -6.6893, -38.5239, -3.6573, -10.033899, -36.1112, -2.8741999, -10.033899, -38.5239, -3.6573, -6.6893, -36.1112, -2.8741999, -10.033899, -36.1158, -2.8757, -6.6893, -36.1112, -2.8741999, -10.033899, -33.3313, -1.9719, -6.6893, -36.1158, -2.8757, -6.6893, -36.106598, -2.8727, -13.378599, -33.3214, -1.9686999, -10.033899, -36.1112, -2.8741999, -10.033899, -36.1112, -2.8741999, -10.033899, -33.3214, -1.9686999, -10.033899, -33.3313, -1.9719, -6.6893, -36.106598, -2.8727, -13.378599, -33.3115, -1.9655, -13.378599, -33.3214, -1.9686999, -10.033899, -33.3214, -1.9686999, -10.033899, -30.2685, -0.9777, -6.6893, -33.3313, -1.9719, -6.6893, -33.3115, -1.9655, -13.378599, -30.252699, -0.9726, -10.033899, -33.3214, -1.9686999, -10.033899, -33.3214, -1.9686999, -10.033899, -30.252699, -0.9726, -10.033899, -30.2685, -0.9777, -6.6893, -30.252699, -0.9726, -10.033899, -29.294899, -0.6617, -6.6893, -30.2685, -0.9777, -6.6893, -33.3115, -1.9655, -13.378599, -30.237001, -0.9675, -13.378599, -30.252699, -0.9726, -10.033899, -30.252699, -0.9726, -10.033899, -29.294899, -0.6617, -10.033899, -29.294899, -0.6617, -6.6893, -30.237001, -0.9675, -13.378599, -29.294899, -0.6617, -10.033899, -30.252699, -0.9726, -10.033899, -30.237001, -0.9675, -13.378599, -29.294899, -0.6617, -13.378599, -29.294899, -0.6617, -10.033899, -38.5239, -3.6573, -3.3446002, -36.1158, -2.8757, -6.6893, -36.1204, -2.9744, -3.3446002, -38.5239, -3.6573, -3.3446002, -38.5239, -3.6573, -6.6893, -36.1158, -2.8757, -6.6893, -38.5239, -3.6573, 0, -36.1204, -2.9744, -3.3446002, -36.125, -2.9757, 0, -38.5239, -3.6573, 0, -38.5239, -3.6573, -3.3446002, -36.1204, -2.9744, -3.3446002, -36.1204, -2.9744, -3.3446002, -33.3512, -2.1875, 0, -36.125, -2.9757, 0, -36.1204, -2.9744, -3.3446002, -33.341198, -2.1847, -3.3446002, -33.3512, -2.1875, 0, -33.341198, -2.1847, -3.3446002, -30.300001, -1.3205999, 0, -33.3512, -2.1875, 0, -33.341198, -2.1847, -3.3446002, -30.2842, -1.3161, -3.3446002, -30.300001, -1.3205999, 0, -30.2842, -1.3161, -3.3446002, -27.9812, -0.6617, 0, -30.300001, -1.3205999, 0, -30.2842, -1.3161, -3.3446002, -27.9812, -0.6617, -3.3446002, -27.9812, -0.6617, 0, -29.294899, -0.6617, 3.3446, -30.3315, -0.9982, 6.6893, -30.315699, -0.9931, 3.3446, -29.294899, -0.6617, 3.3446, -29.294899, -0.6617, 6.6893, -30.3315, -0.9982, 6.6893, -29.294899, -0.6617, 10.033899, -30.3315, -0.9982, 6.6893, -29.294899, -0.6617, 6.6893, -30.3315, -0.9982, 6.6893, -29.294899, -0.6617, 10.033899, -29.294899, -0.6617, 13.378601, -30.3315, -0.9982, 6.6893, -33.3611, -1.9814999, 3.3446, -30.315699, -0.9931, 3.3446, -30.3315, -0.9982, 6.6893, -29.294899, -0.6617, 13.378601, -30.363, -1.0084, 13.378601, -30.3315, -0.9982, 6.6893, -33.371, -1.9848, 6.6893, -33.3611, -1.9814999, 3.3446, -30.363, -1.0084, 13.378601, -33.371, -1.9848, 6.6893, -30.3315, -0.9982, 6.6893, -33.371, -1.9848, 6.6893, -36.1296, -2.8801, 3.3446, -33.3611, -1.9814999, 3.3446, -30.363, -1.0084, 13.378601, -33.3908, -1.9912, 13.378601, -33.371, -1.9848, 6.6893, -33.371, -1.9848, 6.6893, -36.134197, -2.8816, 6.6893, -36.1296, -2.8801, 3.3446, -33.3908, -1.9912, 13.378601, -36.134197, -2.8816, 6.6893, -33.371, -1.9848, 6.6893, -36.134197, -2.8816, 6.6893, -38.5239, -3.6573, 3.3446, -36.1296, -2.8801, 3.3446, -33.3908, -1.9912, 13.378601, -36.1433, -2.8846, 13.378601, -36.134197, -2.8816, 6.6893, -36.134197, -2.8816, 6.6893, -38.5239, -3.6573, 6.6893, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 13.378601, -36.134197, -2.8816, 6.6893, -36.1433, -2.8846, 13.378601, -36.134197, -2.8816, 6.6893, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 6.6893, -36.134197, -2.8816, 6.6893, -38.5239, -3.6573, 13.378601, -38.5239, -3.6573, 10.033899, -29.294899, -1.0265, 16.7232, -30.394499, -1.8736, 20.0679, -30.378698, -1.3354, 16.7232, -29.294899, -1.0265, 16.7232, -26.7572, -1.0756, 20.0679, -30.394499, -1.8736, 20.0679, -26.7572, -1.0756, 20.0679, -30.4102, -1.8771, 23.412498, -30.394499, -1.8736, 20.0679, -26.7572, -1.0756, 20.0679, -26.7572, -1.0756, 23.412498, -30.4102, -1.8771, 23.412498, -30.4102, -1.8771, 23.412498, -33.4106, -2.5354, 20.0679, -30.394499, -1.8736, 20.0679, -30.4102, -1.8771, 23.412498, -33.420498, -2.5376, 23.412498, -33.4106, -2.5354, 20.0679, -33.420498, -2.5376, 23.412498, -36.1525, -3.1369998, 20.0679, -33.4106, -2.5354, 20.0679, -33.420498, -2.5376, 23.412498, -36.1571, -3.138, 23.412498, -36.1525, -3.1369998, 20.0679, -36.1571, -3.138, 23.412498, -38.5239, -3.6573, 20.0679, -36.1525, -3.1369998, 20.0679, -36.1571, -3.138, 23.412498, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 20.0679, -26.7572, -1.0756, 23.412498, -30.426, -2.0144, 26.7572, -30.4102, -1.8771, 23.412498, -26.7572, -1.0756, 23.412498, -26.7572, -1.2701, 26.7572, -30.426, -2.0144, 26.7572, -29.294899, -0.6617, 13.378601, -30.378698, -1.3354, 16.7232, -30.363, -1.0084, 13.378601, -29.294899, -0.6617, 13.378601, -29.294899, -1.0265, 16.7232, -30.378698, -1.3354, 16.7232, -30.173899, -3.2568, -26.7572, -26.7572, -2.7663999, -23.412498, -30.1897, -3.0263, -23.412498, -30.173899, -3.2568, -26.7572, -26.7572, -3.0929, -26.7572, -26.7572, -2.7663999, -23.412498, -30.426, -2.0144, 26.7572, -33.420498, -2.5376, 23.412498, -30.4102, -1.8771, 23.412498, -30.426, -2.0144, 26.7572, -33.4304, -2.6239998, 26.7572, -33.420498, -2.5376, 23.412498, -30.219301, -2.2554998, -17.1196, -29.294899, -0.6617, -13.378599, -30.237001, -0.9675, -13.378599, -30.219301, -2.2554998, -17.1196, -26.7572, -1.6710999, -17.1511, -29.294899, -0.6617, -13.378599, -30.394499, -1.8736, 20.0679, -33.4007, -2.1969, 16.7232, -30.378698, -1.3354, 16.7232, -30.394499, -1.8736, 20.0679, -33.4106, -2.5354, 20.0679, -33.4007, -2.1969, 16.7232, -30.1897, -3.0263, -23.412498, -26.7572, -1.6710999, -20.4958, -30.2036, -2.2528, -20.4644, -30.1897, -3.0263, -23.412498, -26.7572, -2.7663999, -23.412498, -26.7572, -1.6710999, -20.4958, -30.315699, -0.9931, 3.3446, -33.3512, -2.1875, 0, -30.300001, -1.3205999, 0, -30.315699, -0.9931, 3.3446, -33.3611, -1.9814999, 3.3446, -33.3512, -2.1875, 0, -30.2685, -0.9777, -6.6893, -27.9812, -0.6617, -3.3446002, -30.2842, -1.3161, -3.3446002, -30.2685, -0.9777, -6.6893, -29.294899, -0.6617, -6.6893, -27.9812, -0.6617, -3.3446002, -30.378698, -1.3354, 16.7232, -33.3908, -1.9912, 13.378601, -30.363, -1.0084, 13.378601, -30.378698, -1.3354, 16.7232, -33.4007, -2.1969, 16.7232, -33.3908, -1.9912, 13.378601, -33.2719, -3.4053998, -26.7572, -30.1897, -3.0263, -23.412498, -33.2818, -3.2603998, -23.412498, -33.2719, -3.4053998, -26.7572, -30.173899, -3.2568, -26.7572, -30.1897, -3.0263, -23.412498, -33.4007, -2.1969, 16.7232, -36.1433, -2.8846, 13.378601, -33.3908, -1.9912, 13.378601, -33.4007, -2.1969, 16.7232, -36.1479, -2.98, 16.7232, -36.1433, -2.8846, 13.378601, -33.4304, -2.6239998, 26.7572, -36.1571, -3.138, 23.412498, -33.420498, -2.5376, 23.412498, -33.4304, -2.6239998, 26.7572, -36.161697, -3.1780999, 26.7572, -36.1571, -3.138, 23.412498, -33.3005, -2.7756, -17.0916, -30.237001, -0.9675, -13.378599, -33.3115, -1.9655, -13.378599, -33.3005, -2.7756, -17.0916, -30.219301, -2.2554998, -17.1196, -30.237001, -0.9675, -13.378599, -33.2818, -3.2603998, -23.412498, -30.2036, -2.2528, -20.4644, -33.2906, -2.7739, -20.4364, -33.2818, -3.2603998, -23.412498, -30.1897, -3.0263, -23.412498, -30.2036, -2.2528, -20.4644, -33.3611, -1.9814999, 3.3446, -36.125, -2.9757, 0, -33.3512, -2.1875, 0, -33.3611, -1.9814999, 3.3446, -36.1296, -2.8801, 3.3446, -36.125, -2.9757, 0, -33.4106, -2.5354, 20.0679, -36.1479, -2.98, 16.7232, -33.4007, -2.1969, 16.7232, -33.4106, -2.5354, 20.0679, -36.1525, -3.1369998, 20.0679, -36.1479, -2.98, 16.7232, -33.3313, -1.9719, -6.6893, -30.2842, -1.3161, -3.3446002, -33.341198, -2.1847, -3.3446002, -33.3313, -1.9719, -6.6893, -30.2685, -0.9777, -6.6893, -30.2842, -1.3161, -3.3446002, -36.161697, -3.1780999, 26.7572, -38.5239, -3.6573, 23.412498, -36.1571, -3.138, 23.412498, -36.161697, -3.1780999, 26.7572, -38.5239, -3.6573, 26.7572, -38.5239, -3.6573, 23.412498, -36.101498, -3.2484, -17.0662, -33.3115, -1.9655, -13.378599, -36.106598, -2.8727, -13.378599, -36.101498, -3.2484, -17.0662, -33.3005, -2.7756, -17.0916, -33.3115, -1.9655, -13.378599, -36.0928, -3.4731998, -23.412498, -33.2906, -2.7739, -20.4364, -36.0969, -3.2475998, -20.410799, -36.0928, -3.4731998, -23.412498, -33.2818, -3.2603998, -23.412498, -33.2906, -2.7739, -20.4364, -36.1296, -2.8801, 3.3446, -38.5239, -3.6573, 0, -36.125, -2.9757, 0, -36.1296, -2.8801, 3.3446, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 0, -36.1158, -2.8757, -6.6893, -33.341198, -2.1847, -3.3446002, -36.1204, -2.9744, -3.3446002, -36.1158, -2.8757, -6.6893, -33.3313, -1.9719, -6.6893, -33.341198, -2.1847, -3.3446002, -36.1525, -3.1369998, 20.0679, -38.5239, -3.6573, 16.7232, -36.1479, -2.98, 16.7232, -36.1525, -3.1369998, 20.0679, -38.5239, -3.6573, 20.0679, -38.5239, -3.6573, 16.7232, -36.0882, -3.5405, -26.7572, -33.2818, -3.2603998, -23.412498, -36.0928, -3.4731998, -23.412498, -36.0882, -3.5405, -26.7572, -33.2719, -3.4053998, -26.7572, -33.2818, -3.2603998, -23.412498, -36.1479, -2.98, 16.7232, -38.5239, -3.6573, 13.378601, -36.1433, -2.8846, 13.378601, -36.1479, -2.98, 16.7232, -38.5239, -3.6573, 16.7232, -38.5239, -3.6573, 13.378601, -41.627, -4.8403997, 26.7572, -38.5239, -3.6573, 26.7572, -40.2616, -4.8514, 28.2242, -44.537098, -5.95, 26.7572, -41.627, -4.8403997, 26.7572, -43.2833, -5.9677997, 28.143099, -47.4473, -7.0596, 26.7572, -44.537098, -5.95, 26.7572, -46.202698, -7.0731997, 28.132399, -53.117798, -8.6939, 26.7572, -50.290497, -8.1437, 26.7572, -51.906998, -8.6923, 28.058699, -55.5951, -9.176, 26.7572, -53.117798, -8.6939, 26.7572, -54.5561, -9.1761, 27.885199, -58.463497, -9.7343, 26.7572, -55.5951, -9.176, 26.7572, -57.2836, -9.7381, 28.062199, -33.0801, -3.7473, 32.6325, -32.8819, -4.9104, 36.1535, -34.7309, -4.8956, 34.1667, -33.0801, -3.7473, 32.6325, -31.247301, -3.7775998, 34.6107, -32.8819, -4.9104, 36.1535, -34.7309, -4.8956, 34.1667, -34.5837, -6.0910997, 37.759598, -36.416798, -6.0650997, 35.7334, -34.7309, -4.8956, 34.1667, -32.8819, -4.9104, 36.1535, -34.5837, -6.0910997, 37.759598, -36.416798, -6.0650997, 35.7334, -36.1511, -7.1832, 39.2388, -37.9957, -7.1629996, 37.2007, -36.416798, -6.0650997, 35.7334, -34.5837, -6.0910997, 37.759598, -36.1511, -7.1832, 39.2388, -37.9957, -7.1629996, 37.2007, -37.5247, -8.1437, 40.5352, -39.4027, -8.1437, 38.5083, -37.9957, -7.1629996, 37.2007, -36.1511, -7.1832, 39.2388, -37.5247, -8.1437, 40.5352, -39.4027, -8.1437, 38.5083, -39.0032, -8.6748, 41.930298, -40.895798, -8.6774, 39.8957, -39.4027, -8.1437, 38.5083, -37.5247, -8.1437, 40.5352, -39.0032, -8.6748, 41.930298, -40.895798, -8.6774, 39.8957, -40.403297, -9.1775, 43.2515, -42.2959, -9.1772995, 41.196598, -40.895798, -8.6774, 39.8957, -39.0032, -8.6748, 41.930298, -40.403297, -9.1775, 43.2515, -42.2959, -9.1772995, 41.196598, -42.1009, -9.787, 44.8533, -43.9867, -9.7809, 42.7677, -42.2959, -9.1772995, 41.196598, -40.403297, -9.1775, 43.2515, -42.1009, -9.787, 44.8533, -43.9867, -9.7809, 42.7677, -43.8985, -10.4337, 46.549698, -45.8121, -10.4337, 44.463898, -43.9867, -9.7809, 42.7677, -42.1009, -9.787, 44.8533, -43.8985, -10.4337, 46.549698, -31.247301, -3.7775998, 34.6107, -31.153502, -4.9242997, 38.010498, -32.8819, -4.9104, 36.1535, -31.247301, -3.7775998, 34.6107, -29.5455, -3.8058, 36.4474, -31.153502, -4.9242997, 38.010498, -32.8819, -4.9104, 36.1535, -32.857998, -6.1155996, 39.6672, -34.5837, -6.0910997, 37.759598, -32.8819, -4.9104, 36.1535, -31.153502, -4.9242997, 38.010498, -32.857998, -6.1155996, 39.6672, -34.5837, -6.0910997, 37.759598, -34.403698, -7.2022996, 41.169598, -36.1511, -7.1832, 39.2388, -34.5837, -6.0910997, 37.759598, -32.857998, -6.1155996, 39.6672, -34.403698, -7.2022996, 41.169598, -36.1511, -7.1832, 39.2388, -35.7366, -8.1437, 42.4652, -37.5247, -8.1437, 40.5352, -36.1511, -7.1832, 39.2388, -34.403698, -7.2022996, 41.169598, -35.7366, -8.1437, 42.4652, -37.5247, -8.1437, 40.5352, -37.1909, -8.672299, 43.878498, -39.0032, -8.6748, 41.930298, -37.5247, -8.1437, 40.5352, -35.7366, -8.1437, 42.4652, -37.1909, -8.672299, 43.878498, -42.1009, -9.787, 44.8533, -42.032497, -10.4337, 48.5836, -43.8985, -10.4337, 46.549698, -42.1009, -9.787, 44.8533, -40.2735, -9.7929, 46.8743, -42.032497, -10.4337, 48.5836, -39.0032, -8.6748, 41.930298, -38.5812, -9.1777, 45.2297, -40.403297, -9.1775, 43.2515, -39.0032, -8.6748, 41.930298, -37.1909, -8.672299, 43.878498, -38.5812, -9.1777, 45.2297, -40.403297, -9.1775, 43.2515, -40.2735, -9.7929, 46.8743, -42.1009, -9.787, 44.8533, -40.403297, -9.1775, 43.2515, -38.5812, -9.1777, 45.2297, -40.2735, -9.7929, 46.8743, -40.2735, -9.7929, 46.8743, -40.572998, -10.4337, 50.1744, -42.032497, -10.4337, 48.5836, -40.2735, -9.7929, 46.8743, -38.8322, -9.7975, 48.4683, -40.572998, -10.4337, 50.1744, -32.857998, -6.1155996, 39.6672, -32.985, -7.2179, 42.7371, -34.403698, -7.2022996, 41.169598, -32.857998, -6.1155996, 39.6672, -31.445799, -6.1355996, 41.2283, -32.985, -7.2179, 42.7371, -38.5812, -9.1777, 45.2297, -38.8322, -9.7975, 48.4683, -40.2735, -9.7929, 46.8743, -38.5812, -9.1777, 45.2297, -37.1326, -9.1778, 46.802498, -38.8322, -9.7975, 48.4683, -34.403698, -7.2022996, 41.169598, -34.2949, -8.1437, 44.0212, -35.7366, -8.1437, 42.4652, -34.403698, -7.2022996, 41.169598, -32.985, -7.2179, 42.7371, -34.2949, -8.1437, 44.0212, -35.7366, -8.1437, 42.4652, -35.7403, -8.6704, 45.4379, -37.1909, -8.672299, 43.878498, -35.7366, -8.1437, 42.4652, -34.2949, -8.1437, 44.0212, -35.7403, -8.6704, 45.4379, -29.5455, -3.8058, 36.4474, -29.727, -4.9357, 39.5433, -31.153502, -4.9242997, 38.010498, -29.5455, -3.8058, 36.4474, -28.1288, -3.8291998, 37.976498, -29.727, -4.9357, 39.5433, -37.1909, -8.672299, 43.878498, -37.1326, -9.1778, 46.802498, -38.5812, -9.1777, 45.2297, -37.1909, -8.672299, 43.878498, -35.7403, -8.6704, 45.4379, -37.1326, -9.1778, 46.802498, -31.153502, -4.9242997, 38.010498, -31.445799, -6.1355996, 41.2283, -32.857998, -6.1155996, 39.6672, -31.153502, -4.9242997, 38.010498, -29.727, -4.9357, 39.5433, -31.445799, -6.1355996, 41.2283, -28.1288, -3.8291998, 37.976498, -28.3464, -4.9467, 41.0267, -29.727, -4.9357, 39.5433, -28.1288, -3.8291998, 37.976498, -26.7572, -3.8518999, 39.456898, -28.3464, -4.9467, 41.0267, -29.727, -4.9357, 39.5433, -30.0795, -6.1549, 42.738598, -31.445799, -6.1355996, 41.2283, -29.727, -4.9357, 39.5433, -28.3464, -4.9467, 41.0267, -30.0795, -6.1549, 42.738598, -38.8322, -9.7975, 48.4683, -39.1638, -10.4337, 51.7104, -40.572998, -10.4337, 50.1744, -38.8322, -9.7975, 48.4683, -37.44, -9.802, 50.008, -39.1638, -10.4337, 51.7104, -31.445799, -6.1355996, 41.2283, -31.612902, -7.2328997, 44.2532, -32.985, -7.2179, 42.7371, -31.445799, -6.1355996, 41.2283, -30.0795, -6.1549, 42.738598, -31.612902, -7.2328997, 44.2532, -37.1326, -9.1778, 46.802498, -37.44, -9.802, 50.008, -38.8322, -9.7975, 48.4683, -37.1326, -9.1778, 46.802498, -35.7329, -9.177899, 48.322197, -37.44, -9.802, 50.008, -32.985, -7.2179, 42.7371, -32.901, -8.1437, 45.5256, -34.2949, -8.1437, 44.0212, -32.985, -7.2179, 42.7371, -31.612902, -7.2328997, 44.2532, -32.901, -8.1437, 45.5256, -34.2949, -8.1437, 44.0212, -34.3383, -8.6685, 46.945, -35.7403, -8.6704, 45.4379, -34.2949, -8.1437, 44.0212, -32.901, -8.1437, 45.5256, -34.3383, -8.6685, 46.945, -35.7403, -8.6704, 45.4379, -35.7329, -9.177899, 48.322197, -37.1326, -9.1778, 46.802498, -35.7403, -8.6704, 45.4379, -34.3383, -8.6685, 46.945, -35.7329, -9.177899, 48.322197, -34.3383, -8.6685, 46.945, -34.0797, -9.1781, 50.1171, -35.7329, -9.177899, 48.322197, -34.3383, -8.6685, 46.945, -32.6904, -8.6662, 48.7166, -34.0797, -9.1781, 50.1171, -26.7572, -3.8518999, 39.456898, -26.7572, -4.9595, 42.7343, -28.3464, -4.9467, 41.0267, -28.3464, -4.9467, 41.0267, -28.496698, -6.1773996, 44.4883, -30.0795, -6.1549, 42.738598, -28.3464, -4.9467, 41.0267, -26.7572, -4.9595, 42.7343, -28.496698, -6.1773996, 44.4883, -37.44, -9.802, 50.008, -37.4804, -10.4337, 53.5453, -39.1638, -10.4337, 51.7104, -37.44, -9.802, 50.008, -35.786, -9.8073, 51.8372, -37.4804, -10.4337, 53.5453, -30.0795, -6.1549, 42.738598, -30.0148, -7.2503, 46.0191, -31.612902, -7.2328997, 44.2532, -30.0795, -6.1549, 42.738598, -28.496698, -6.1773996, 44.4883, -30.0148, -7.2503, 46.0191, -32.901, -8.1437, 45.5256, -32.6904, -8.6662, 48.7166, -34.3383, -8.6685, 46.945, -32.901, -8.1437, 45.5256, -31.270601, -8.1437, 47.285297, -32.6904, -8.6662, 48.7166, -35.7329, -9.177899, 48.322197, -35.786, -9.8073, 51.8372, -37.44, -9.802, 50.008, -35.7329, -9.177899, 48.322197, -34.0797, -9.1781, 50.1171, -35.786, -9.8073, 51.8372, -31.612902, -7.2328997, 44.2532, -31.270601, -8.1437, 47.285297, -32.901, -8.1437, 45.5256, -31.612902, -7.2328997, 44.2532, -30.0148, -7.2503, 46.0191, -31.270601, -8.1437, 47.285297, -26.7572, -4.9595, 42.7343, -26.7572, -6.2019997, 46.4111, -28.496698, -6.1773996, 44.4883, -35.786, -9.8073, 51.8372, -35.735397, -10.4337, 55.4473, -37.4804, -10.4337, 53.5453, -35.786, -9.8073, 51.8372, -34.0528, -9.8129, 53.753998, -35.735397, -10.4337, 55.4473, -28.496698, -6.1773996, 44.4883, -28.2762, -7.2693996, 47.940197, -30.0148, -7.2503, 46.0191, -28.496698, -6.1773996, 44.4883, -26.7572, -6.2019997, 46.4111, -28.2762, -7.2693996, 47.940197, -32.6904, -8.6662, 48.7166, -32.3284, -9.1783, 52.0186, -34.0797, -9.1781, 50.1171, -32.6904, -8.6662, 48.7166, -30.9289, -8.663899, 50.6101, -32.3284, -9.1783, 52.0186, -31.270601, -8.1437, 47.285297, -30.9289, -8.663899, 50.6101, -32.6904, -8.6662, 48.7166, -31.270601, -8.1437, 47.285297, -29.511599, -8.1437, 49.1838, -30.9289, -8.663899, 50.6101, -34.0797, -9.1781, 50.1171, -34.0528, -9.8129, 53.753998, -35.786, -9.8073, 51.8372, -34.0797, -9.1781, 50.1171, -32.3284, -9.1783, 52.0186, -34.0528, -9.8129, 53.753998, -30.0148, -7.2503, 46.0191, -29.511599, -8.1437, 49.1838, -31.270601, -8.1437, 47.285297, -30.0148, -7.2503, 46.0191, -28.2762, -7.2693996, 47.940197, -29.511599, -8.1437, 49.1838, -28.2762, -7.2693996, 47.940197, -27.9641, -8.1437, 50.853897, -29.511599, -8.1437, 49.1838, -28.2762, -7.2693996, 47.940197, -26.7572, -7.286, 49.6186, -27.9641, -8.1437, 50.853897, -34.0528, -9.8129, 53.753998, -34.1461, -10.4337, 57.1796, -35.735397, -10.4337, 55.4473, -34.0528, -9.8129, 53.753998, -32.4888, -9.8179, 55.4837, -34.1461, -10.4337, 57.1796, -26.7572, -6.2019997, 46.4111, -26.7572, -7.286, 49.6186, -28.2762, -7.2693996, 47.940197, -30.9289, -8.663899, 50.6101, -30.7632, -9.1784, 53.718, -32.3284, -9.1783, 52.0186, -30.9289, -8.663899, 50.6101, -29.3669, -8.6617, 52.2893, -30.7632, -9.1784, 53.718, -29.511599, -8.1437, 49.1838, -29.3669, -8.6617, 52.2893, -30.9289, -8.663899, 50.6101, -29.511599, -8.1437, 49.1838, -27.9641, -8.1437, 50.853897, -29.3669, -8.6617, 52.2893, -32.3284, -9.1783, 52.0186, -32.4888, -9.8179, 55.4837, -34.0528, -9.8129, 53.753998, -32.3284, -9.1783, 52.0186, -30.7632, -9.1784, 53.718, -32.4888, -9.8179, 55.4837, -29.3669, -8.6617, 52.2893, -29.525799, -9.1785, 55.061497, -30.7632, -9.1784, 53.718, -29.3669, -8.6617, 52.2893, -28.140299, -8.6601, 53.6078, -29.525799, -9.1785, 55.061497, -27.9641, -8.1437, 50.853897, -28.140299, -8.6601, 53.6078, -29.3669, -8.6617, 52.2893, -27.9641, -8.1437, 50.853897, -26.7572, -8.1437, 52.156597, -28.140299, -8.6601, 53.6078, -30.7632, -9.1784, 53.718, -31.2423, -9.821899, 56.862297, -32.4888, -9.8179, 55.4837, -30.7632, -9.1784, 53.718, -29.525799, -9.1785, 55.061497, -31.2423, -9.821899, 56.862297, -26.7572, -7.286, 49.6186, -26.7572, -8.1437, 52.156597, -27.9641, -8.1437, 50.853897, -32.4888, -9.8179, 55.4837, -32.870197, -10.4337, 58.570297, -34.1461, -10.4337, 57.1796, -32.4888, -9.8179, 55.4837, -31.2423, -9.821899, 56.862297, -32.870197, -10.4337, 58.570297, -28.140299, -8.6601, 53.6078, -28.1322, -9.1786995, 56.5746, -29.525799, -9.1785, 55.061497, -28.140299, -8.6601, 53.6078, -26.7572, -8.658199, 55.0947, -28.1322, -9.1786995, 56.5746, -26.7572, -8.1437, 52.156597, -26.7572, -8.658199, 55.0947, -28.140299, -8.6601, 53.6078, -29.525799, -9.1785, 55.061497, -29.8403, -9.8265, 58.4128, -31.2423, -9.821899, 56.862297, -29.525799, -9.1785, 55.061497, -28.1322, -9.1786995, 56.5746, -29.8403, -9.8265, 58.4128, -31.2423, -9.821899, 56.862297, -31.437601, -10.4337, 60.131798, -32.870197, -10.4337, 58.570297, -31.2423, -9.821899, 56.862297, -29.8403, -9.8265, 58.4128, -31.437601, -10.4337, 60.131798, -26.7572, -8.658199, 55.0947, -26.7572, -9.1788, 58.067497, -28.1322, -9.1786995, 56.5746, -28.1322, -9.1786995, 56.5746, -28.4674, -9.8309, 59.9312, -29.8403, -9.8265, 58.4128, -28.1322, -9.1786995, 56.5746, -26.7572, -9.1788, 58.067497, -28.4674, -9.8309, 59.9312, -29.8403, -9.8265, 58.4128, -30.044802, -10.4337, 61.649998, -31.437601, -10.4337, 60.131798, -29.8403, -9.8265, 58.4128, -28.4674, -9.8309, 59.9312, -30.044802, -10.4337, 61.649998, -28.4674, -9.8309, 59.9312, -28.2834, -10.4337, 63.569798, -30.044802, -10.4337, 61.649998, -28.4674, -9.8309, 59.9312, -26.7572, -9.8364, 61.822598, -28.2834, -10.4337, 63.569798, -26.7572, -9.1788, 58.067497, -26.7572, -9.8364, 61.822598, -28.4674, -9.8309, 59.9312, -26.7572, -9.8364, 61.822598, -26.7572, -10.4337, 65.2334, -28.2834, -10.4337, 63.569798, -39.7387, -7.144, 35.2747, -39.4027, -8.1437, 38.5083, -41.179398, -8.1437, 36.5907, -39.7387, -7.144, 35.2747, -37.9957, -7.1629996, 37.2007, -39.4027, -8.1437, 38.5083, -34.806297, -3.7187998, 30.769499, -34.7309, -4.8956, 34.1667, -36.474598, -4.8817, 32.293198, -34.806297, -3.7187998, 30.769499, -33.0801, -3.7473, 32.6325, -34.7309, -4.8956, 34.1667, -45.7693, -9.7752, 40.7963, -45.8121, -10.4337, 44.463898, -47.620697, -10.4337, 42.4927, -45.7693, -9.7752, 40.7963, -43.9867, -9.7809, 42.7677, -45.8121, -10.4337, 44.463898, -36.474598, -4.8817, 32.293198, -36.416798, -6.0650997, 35.7334, -38.147, -6.0406, 33.8208, -36.474598, -4.8817, 32.293198, -34.7309, -4.8956, 34.1667, -36.416798, -6.0650997, 35.7334, -38.147, -6.0406, 33.8208, -37.9957, -7.1629996, 37.2007, -39.7387, -7.144, 35.2747, -38.147, -6.0406, 33.8208, -36.416798, -6.0650997, 35.7334, -37.9957, -7.1629996, 37.2007, -41.179398, -8.1437, 36.5907, -40.895798, -8.6774, 39.8957, -42.6861, -8.6798, 37.9712, -41.179398, -8.1437, 36.5907, -39.4027, -8.1437, 38.5083, -40.895798, -8.6774, 39.8957, -42.6861, -8.6798, 37.9712, -42.2959, -9.1772995, 41.196598, -44.0855, -9.1771, 39.253498, -42.6861, -8.6798, 37.9712, -40.895798, -8.6774, 39.8957, -42.2959, -9.1772995, 41.196598, -44.0855, -9.1771, 39.253498, -43.9867, -9.7809, 42.7677, -45.7693, -9.7752, 40.7963, -44.0855, -9.1771, 39.253498, -42.2959, -9.1772995, 41.196598, -43.9867, -9.7809, 42.7677, -41.209297, -7.1278996, 33.6499, -41.179398, -8.1437, 36.5907, -42.6902, -8.1437, 34.960197, -41.209297, -7.1278996, 33.6499, -39.7387, -7.144, 35.2747, -41.179398, -8.1437, 36.5907, -36.2241, -3.6952999, 29.2393, -36.474598, -4.8817, 32.293198, -37.9203, -4.8701, 30.739899, -36.2241, -3.6952999, 29.2393, -34.806297, -3.7187998, 30.769499, -36.474598, -4.8817, 32.293198, -47.322998, -9.7702, 39.078, -47.620697, -10.4337, 42.4927, -49.212498, -10.4337, 40.7576, -47.322998, -9.7702, 39.078, -45.7693, -9.7752, 40.7963, -47.620697, -10.4337, 42.4927, -37.9203, -4.8701, 30.739899, -38.147, -6.0406, 33.8208, -39.5943, -6.0200996, 32.221, -37.9203, -4.8701, 30.739899, -36.474598, -4.8817, 32.293198, -38.147, -6.0406, 33.8208, -39.5943, -6.0200996, 32.221, -39.7387, -7.144, 35.2747, -41.209297, -7.1278996, 33.6499, -39.5943, -6.0200996, 32.221, -38.147, -6.0406, 33.8208, -39.7387, -7.144, 35.2747, -42.6902, -8.1437, 34.960197, -42.6861, -8.6798, 37.9712, -44.2212, -8.6819, 36.321, -42.6902, -8.1437, 34.960197, -41.179398, -8.1437, 36.5907, -42.6861, -8.6798, 37.9712, -44.2212, -8.6819, 36.321, -44.0855, -9.1771, 39.253498, -45.6317, -9.177, 37.5747, -44.2212, -8.6819, 36.321, -42.6861, -8.6798, 37.9712, -44.0855, -9.1771, 39.253498, -45.6317, -9.177, 37.5747, -45.7693, -9.7752, 40.7963, -47.322998, -9.7702, 39.078, -45.6317, -9.177, 37.5747, -44.0855, -9.1771, 39.253498, -45.7693, -9.7752, 40.7963, -42.4946, -7.1138, 32.2296, -42.6902, -8.1437, 34.960197, -44.0089, -8.1437, 33.5369, -42.4946, -7.1138, 32.2296, -41.209297, -7.1278996, 33.6499, -42.6902, -8.1437, 34.960197, -37.4711, -3.6747, 27.8934, -37.9203, -4.8701, 30.739899, -39.1893, -4.8599997, 29.3764, -37.4711, -3.6747, 27.8934, -36.2241, -3.6952999, 29.2393, -37.9203, -4.8701, 30.739899, -48.6723, -9.7657995, 37.585697, -49.212498, -10.4337, 40.7576, -50.5928, -10.4337, 39.2532, -48.6723, -9.7657995, 37.585697, -47.322998, -9.7702, 39.078, -49.212498, -10.4337, 40.7576, -39.1893, -4.8599997, 29.3764, -39.5943, -6.0200996, 32.221, -40.8617, -6.0021, 30.82, -39.1893, -4.8599997, 29.3764, -37.9203, -4.8701, 30.739899, -39.5943, -6.0200996, 32.221, -40.8617, -6.0021, 30.82, -41.209297, -7.1278996, 33.6499, -42.4946, -7.1138, 32.2296, -40.8617, -6.0021, 30.82, -39.5943, -6.0200996, 32.221, -41.209297, -7.1278996, 33.6499, -44.0089, -8.1437, 33.5369, -44.2212, -8.6819, 36.321, -45.559097, -8.6837, 34.882698, -44.0089, -8.1437, 33.5369, -42.6902, -8.1437, 34.960197, -44.2212, -8.6819, 36.321, -45.559097, -8.6837, 34.882698, -45.6317, -9.177, 37.5747, -46.9771, -9.1769, 36.114, -45.559097, -8.6837, 34.882698, -44.2212, -8.6819, 36.321, -45.6317, -9.177, 37.5747, -46.9771, -9.1769, 36.114, -47.322998, -9.7702, 39.078, -48.6723, -9.7657995, 37.585697, -46.9771, -9.1769, 36.114, -45.6317, -9.177, 37.5747, -47.322998, -9.7702, 39.078, -43.5821, -7.1018996, 31.028, -44.0089, -8.1437, 33.5369, -45.125797, -8.1437, 32.3315, -43.5821, -7.1018996, 31.028, -42.4946, -7.1138, 32.2296, -44.0089, -8.1437, 33.5369, -38.5239, -3.6573, 26.7572, -39.1893, -4.8599997, 29.3764, -40.2616, -4.8514, 28.2242, -38.5239, -3.6573, 26.7572, -37.4711, -3.6747, 27.8934, -39.1893, -4.8599997, 29.3764, -49.816998, -9.7621, 36.319798, -50.5928, -10.4337, 39.2532, -51.764698, -10.4337, 37.9757, -49.816998, -9.7621, 36.319798, -48.6723, -9.7657995, 37.585697, -50.5928, -10.4337, 39.2532, -40.2616, -4.8514, 28.2242, -40.8617, -6.0021, 30.82, -41.9332, -5.9869, 29.6356, -40.2616, -4.8514, 28.2242, -39.1893, -4.8599997, 29.3764, -40.8617, -6.0021, 30.82, -41.9332, -5.9869, 29.6356, -42.4946, -7.1138, 32.2296, -43.5821, -7.1018996, 31.028, -41.9332, -5.9869, 29.6356, -40.8617, -6.0021, 30.82, -42.4946, -7.1138, 32.2296, -45.125797, -8.1437, 32.3315, -45.559097, -8.6837, 34.882698, -46.692997, -8.6852, 33.6638, -45.125797, -8.1437, 32.3315, -44.0089, -8.1437, 33.5369, -45.559097, -8.6837, 34.882698, -46.692997, -8.6852, 33.6638, -46.9771, -9.1769, 36.114, -48.118, -9.1768, 34.875298, -46.692997, -8.6852, 33.6638, -45.559097, -8.6837, 34.882698, -46.9771, -9.1769, 36.114, -48.118, -9.1768, 34.875298, -48.6723, -9.7657995, 37.585697, -49.816998, -9.7621, 36.319798, -48.118, -9.1768, 34.875298, -46.9771, -9.1769, 36.114, -48.6723, -9.7657995, 37.585697, -44.939, -7.0871, 29.5287, -45.125797, -8.1437, 32.3315, -46.506897, -8.1437, 30.840899, -44.939, -7.0871, 29.5287, -43.5821, -7.1018996, 31.028, -45.125797, -8.1437, 32.3315, -51.1965, -9.7577, 34.794, -51.764698, -10.4337, 37.9757, -53.163, -10.4337, 36.4517, -51.1965, -9.7577, 34.794, -49.816998, -9.7621, 36.319798, -51.764698, -10.4337, 37.9757, -41.627, -4.8403997, 26.7572, -41.9332, -5.9869, 29.6356, -43.2833, -5.9677997, 28.143099, -41.627, -4.8403997, 26.7572, -40.2616, -4.8514, 28.2242, -41.9332, -5.9869, 29.6356, -43.2833, -5.9677997, 28.143099, -43.5821, -7.1018996, 31.028, -44.939, -7.0871, 29.5287, -43.2833, -5.9677997, 28.143099, -41.9332, -5.9869, 29.6356, -43.5821, -7.1018996, 31.028, -46.506897, -8.1437, 30.840899, -46.692997, -8.6852, 33.6638, -48.083, -8.687099, 32.1695, -46.506897, -8.1437, 30.840899, -45.125797, -8.1437, 32.3315, -46.692997, -8.6852, 33.6638, -48.083, -8.687099, 32.1695, -48.118, -9.1768, 34.875298, -49.5056, -9.1765995, 33.3687, -48.083, -8.687099, 32.1695, -46.692997, -8.6852, 33.6638, -48.118, -9.1768, 34.875298, -49.5056, -9.1765995, 33.3687, -49.816998, -9.7621, 36.319798, -51.1965, -9.7577, 34.794, -49.5056, -9.1765995, 33.3687, -48.118, -9.1768, 34.875298, -49.816998, -9.7621, 36.319798, -46.202698, -7.0731997, 28.132399, -46.506897, -8.1437, 30.840899, -47.7968, -8.1437, 29.4487, -46.202698, -7.0731997, 28.132399, -44.939, -7.0871, 29.5287, -46.506897, -8.1437, 30.840899, -52.4946, -9.7535, 33.3584, -53.163, -10.4337, 36.4517, -54.4828, -10.4337, 35.0131, -52.4946, -9.7535, 33.3584, -51.1965, -9.7577, 34.794, -53.163, -10.4337, 36.4517, -44.537098, -5.95, 26.7572, -44.939, -7.0871, 29.5287, -46.202698, -7.0731997, 28.132399, -44.537098, -5.95, 26.7572, -43.2833, -5.9677997, 28.143099, -44.939, -7.0871, 29.5287, -47.7968, -8.1437, 29.4487, -48.083, -8.687099, 32.1695, -49.384697, -8.6889, 30.770199, -47.7968, -8.1437, 29.4487, -46.506897, -8.1437, 30.840899, -48.083, -8.687099, 32.1695, -49.384697, -8.6889, 30.770199, -49.5056, -9.1765995, 33.3687, -50.808, -9.176499, 31.954699, -49.384697, -8.6889, 30.770199, -48.083, -8.687099, 32.1695, -49.5056, -9.1765995, 33.3687, -50.808, -9.176499, 31.954699, -51.1965, -9.7577, 34.794, -52.4946, -9.7535, 33.3584, -50.808, -9.176499, 31.954699, -49.5056, -9.1765995, 33.3687, -51.1965, -9.7577, 34.794, -47.4473, -7.0596, 26.7572, -47.7968, -8.1437, 29.4487, -49.074398, -8.1437, 28.0697, -47.4473, -7.0596, 26.7572, -46.202698, -7.0731997, 28.132399, -47.7968, -8.1437, 29.4487, -53.7992, -9.7493, 31.915699, -54.4828, -10.4337, 35.0131, -55.8173, -10.4337, 33.5585, -53.7992, -9.7493, 31.915699, -52.4946, -9.7535, 33.3584, -54.4828, -10.4337, 35.0131, -49.074398, -8.1437, 28.0697, -49.384697, -8.6889, 30.770199, -50.6806, -8.690599, 29.3771, -49.074398, -8.1437, 28.0697, -47.7968, -8.1437, 29.4487, -49.384697, -8.6889, 30.770199, -50.6806, -8.690599, 29.3771, -50.808, -9.176499, 31.954699, -52.1102, -9.1764, 30.5408, -50.6806, -8.690599, 29.3771, -49.384697, -8.6889, 30.770199, -50.808, -9.176499, 31.954699, -52.1102, -9.1764, 30.5408, -52.4946, -9.7535, 33.3584, -53.7992, -9.7493, 31.915699, -52.1102, -9.1764, 30.5408, -50.808, -9.176499, 31.954699, -52.4946, -9.7535, 33.3584, -50.290497, -8.1437, 26.7572, -50.6806, -8.690599, 29.3771, -51.906998, -8.6923, 28.058699, -50.290497, -8.1437, 26.7572, -49.074398, -8.1437, 28.0697, -50.6806, -8.690599, 29.3771, -51.906998, -8.6923, 28.058699, -52.1102, -9.1764, 30.5408, -53.3363, -9.1763, 29.2095, -51.906998, -8.6923, 28.058699, -50.6806, -8.690599, 29.3771, -52.1102, -9.1764, 30.5408, -55.0202, -9.745399, 30.5653, -55.8173, -10.4337, 33.5585, -57.058, -10.4337, 32.2062, -55.0202, -9.745399, 30.5653, -53.7992, -9.7493, 31.915699, -55.8173, -10.4337, 33.5585, -53.3363, -9.1763, 29.2095, -53.7992, -9.7493, 31.915699, -55.0202, -9.745399, 30.5653, -53.3363, -9.1763, 29.2095, -52.1102, -9.1764, 30.5408, -53.7992, -9.7493, 31.915699, -53.117798, -8.6939, 26.7572, -53.3363, -9.1763, 29.2095, -54.5561, -9.1761, 27.885199, -53.117798, -8.6939, 26.7572, -51.906998, -8.6923, 28.058699, -53.3363, -9.1763, 29.2095, -56.245598, -9.7414, 29.2101, -57.058, -10.4337, 32.2062, -58.316597, -10.4337, 30.834398, -56.245598, -9.7414, 29.2101, -55.0202, -9.745399, 30.5653, -57.058, -10.4337, 32.2062, -54.5561, -9.1761, 27.885199, -55.0202, -9.745399, 30.5653, -56.245598, -9.7414, 29.2101, -54.5561, -9.1761, 27.885199, -53.3363, -9.1763, 29.2095, -55.0202, -9.745399, 30.5653, -57.2836, -9.7381, 28.062199, -58.316597, -10.4337, 30.834398, -59.3761, -10.4337, 29.679499, -57.2836, -9.7381, 28.062199, -56.245598, -9.7414, 29.2101, -58.316597, -10.4337, 30.834398, -55.5951, -9.176, 26.7572, -56.245598, -9.7414, 29.2101, -57.2836, -9.7381, 28.062199, -55.5951, -9.176, 26.7572, -54.5561, -9.1761, 27.885199, -56.245598, -9.7414, 29.2101, -58.463497, -9.7343, 26.7572, -59.3761, -10.4337, 29.679499, -60.611, -10.4337, 28.3335, -58.463497, -9.7343, 26.7572, -57.2836, -9.7381, 28.062199, -59.3761, -10.4337, 29.679499, 20.0676, 0.0021, 3.3446, 23.4123, 0, -0.000100016594, 23.4123, 0, 3.3446, 20.0676, 0.0021, 3.3446, 20.229, 0.030399999, -0.05219996, 23.4123, 0, -0.000100016594, 6.6893, 3.3913999, 3.3446, 10.0336, 3.1929998, -0.000100016594, 10.0336, 3.1929998, 3.3446, 6.6893, 3.3913999, 3.3446, 6.6893, 3.3913999, -0.000100016594, 10.0336, 3.1929998, -0.000100016594, 23.4123, 0, 3.3446, 26.7582, -0.83089995, 6.6893, 23.4123, 0, 6.6893, 23.4123, 0, 3.3446, 26.7575, -0.7312, 3.3446, 26.7582, -0.83089995, 6.6893, 23.4123, 0, 6.6893, 26.7582, -0.83089995, 6.6893, 23.4123, -0.35529998, 10.033899, 20.0676, 0.0021, 3.3446, 20.0676, 0, 10.0338, 17.491, 0.0034999999, 7.408, 17.491, 0.0034999999, 7.408, 14.0257, 0.0442, 7.3251996, 20.0676, 0.0021, 3.3446, 20.0676, 0, 10.0338, 23.4123, 0, 6.6893, 23.4123, -0.35529998, 10.033899, 20.0676, 0.0021, 3.3446, 23.4123, 0, 3.3446, 23.4123, 0, 6.6893, 12.7055, 1.032, 4.0926, 11.277899, 2.3706, -0.000100016594, 14.261799, 0.9069, 1.1804999, 12.7055, 1.032, 4.0926, 11.2782, 2.1174, 3.3446, 11.277899, 2.3706, -0.000100016594, 10.0336, 3.1929998, 3.3446, 11.277899, 2.3706, -0.000100016594, 11.2782, 2.1174, 3.3446, 10.0336, 3.1929998, 3.3446, 10.0336, 3.1929998, -0.000100016594, 11.277899, 2.3706, -0.000100016594, 20.229, 0.030399999, -0.05219996, 20.0676, 0.0021, 3.3446, 14.261799, 0.9069, 1.1804999, 14.0257, 0.0442, 7.3251996, 14.261799, 0.9069, 1.1804999, 20.0676, 0.0021, 3.3446, 12.7055, 1.032, 4.0926, 14.261799, 0.9069, 1.1804999, 14.0257, 0.0442, 7.3251996, 26.7575, -0.7312, 3.3446, 23.4123, 0, 3.3446, 23.4123, 0, -0.000100016594, 20.0676, 0, 10.0338, 20.0676, 0.0021, 3.3446, 23.4123, 0, 6.6893, 17.253021, -20.068901, 7.9789844, 17.227175, -19.735691, 9.261204, 17.026821, -20.231876, 8.073181, 17.253021, -20.068901, 7.9789844, 17.434578, -19.605183, 9.12792, 17.227175, -19.735691, 9.261204, 17.434578, -19.605183, 9.12792, 17.571707, -18.471199, 10.41548, 17.227175, -19.735691, 9.261204, 17.434578, -19.605183, 9.12792, 17.629164, -18.471626, 10.358021, 17.571707, -18.471199, 10.41548, 17.227175, -19.735691, 9.261204, 17.571707, -18.471199, 10.41548, 17.608658, -18.470558, 10.487461, 17.026821, -20.231876, 8.073181, 17.227175, -19.735691, 9.261204, 17.33739, -19.946938, 9.406449, 17.227175, -19.735691, 9.261204, 17.608658, -18.470558, 10.487461, 17.33739, -19.946938, 9.406449, 17.026821, -20.231876, 8.073181, 17.33739, -19.946938, 9.406449, 17.131271, -20.495667, 8.150929, 17.33739, -19.946938, 9.406449, 17.608658, -18.470558, 10.487461, 17.688543, -18.470558, 10.474859, 17.131271, -20.495667, 8.150929, 17.33739, -19.946938, 9.406449, 17.61293, -19.946938, 9.362875, 17.33739, -19.946938, 9.406449, 17.688543, -18.470558, 10.474859, 17.61293, -19.946938, 9.362875, 17.131271, -20.495667, 8.150929, 17.61293, -19.946938, 9.362875, 17.421762, -20.495667, 8.105006, 17.61293, -19.946938, 9.362875, 17.688543, -18.470558, 10.474859, 17.701359, -18.471199, 10.394974, 17.421762, -20.495667, 8.105006, 17.61293, -19.946938, 9.362875, 17.67295, -19.735691, 9.190718, 17.61293, -19.946938, 9.362875, 17.701359, -18.471199, 10.394974, 17.67295, -19.735691, 9.190718, 17.67295, -19.735691, 9.190718, 17.701359, -18.471199, 10.394974, 17.629164, -18.471626, 10.358021, 17.421762, -20.495667, 8.105006, 17.67295, -19.735691, 9.190718, 17.497162, -20.231876, 7.998849, 17.67295, -19.735691, 9.190718, 17.629164, -18.471626, 10.358021, 17.434578, -19.605183, 9.12792, 17.497162, -20.231876, 7.998849, 17.67295, -19.735691, 9.190718, 17.434578, -19.605183, 9.12792, 17.497162, -20.231876, 7.998849, 17.434578, -19.605183, 9.12792, 17.253021, -20.068901, 7.9789844, 16.28308, -22.030998, 7.9881697, 17.28207, -19.97385, 8.213941, 16.7107, -22.030998, 8.612513, 16.28308, -22.030998, 7.9881697, 17.120804, -19.944588, 7.9881687, 17.28207, -19.97385, 8.213941, 17.120804, -19.944588, 7.9881687, 17.36345, -17.907732, 8.087917, 17.28207, -19.97385, 8.213941, 17.120804, -19.944588, 7.9881687, 17.291254, -17.900043, 7.9881682, 17.36345, -17.907732, 8.087917, 17.28207, -19.97385, 8.213941, 17.36345, -17.907732, 8.087917, 17.480074, -17.92012, 8.049685, 16.7107, -22.030998, 8.612513, 17.28207, -19.97385, 8.213941, 17.543299, -20.021269, 8.127647, 17.28207, -19.97385, 8.213941, 17.480074, -17.92012, 8.049685, 17.543299, -20.021269, 8.127647, 16.7107, -22.030998, 8.612513, 17.543299, -20.021269, 8.127647, 17.402325, -22.030998, 8.373924, 17.543299, -20.021269, 8.127647, 17.480074, -17.92012, 8.049685, 17.480074, -17.92012, 7.926439, 17.402325, -22.030998, 8.373924, 17.543299, -20.021269, 8.127647, 17.543299, -20.02127, 7.8486905, 17.543299, -20.021269, 8.127647, 17.480074, -17.92012, 7.926439, 17.543299, -20.02127, 7.8486905, 17.402325, -22.030998, 8.373924, 17.543299, -20.02127, 7.8486905, 17.402325, -22.030998, 7.602201, 17.543299, -20.02127, 7.8486905, 17.480074, -17.92012, 7.926439, 17.36345, -17.907732, 7.8884187, 17.402325, -22.030998, 7.602201, 17.543299, -20.02127, 7.8486905, 17.28207, -19.97385, 7.7623973, 17.543299, -20.02127, 7.8486905, 17.36345, -17.907732, 7.8884187, 17.28207, -19.97385, 7.7623973, 17.28207, -19.97385, 7.7623973, 17.36345, -17.907732, 7.8884187, 17.291254, -17.900043, 7.9881682, 17.402325, -22.030998, 7.602201, 17.28207, -19.97385, 7.7623973, 16.7107, -22.030998, 7.363827, 17.28207, -19.97385, 7.7623973, 17.291254, -17.900043, 7.9881682, 17.120804, -19.944588, 7.9881687, 16.7107, -22.030998, 7.363827, 17.28207, -19.97385, 7.7623973, 17.120804, -19.944588, 7.9881687, 16.7107, -22.030998, 7.363827, 17.120804, -19.944588, 7.9881687, 16.28308, -22.030998, 7.9881697, 17.194283, -19.802975, 7.602841, 18.323994, -19.463997, 6.9496627, 17.395275, -19.968939, 7.750009, 17.194283, -19.802975, 7.602841, 18.09865, -19.331139, 6.83902, 18.323994, -19.463997, 6.9496627, 18.09865, -19.331139, 6.83902, 19.147194, -18.177078, 6.041237, 18.323994, -19.463997, 6.9496627, 18.09865, -19.331139, 6.83902, 19.067095, -18.177505, 6.0213723, 19.147194, -18.177078, 6.041237, 18.323994, -19.463997, 6.9496627, 19.147194, -18.177078, 6.041237, 19.190767, -18.176437, 5.9711766, 17.395275, -19.968939, 7.750009, 18.323994, -19.463997, 6.9496627, 18.392344, -19.679089, 6.777077, 18.323994, -19.463997, 6.9496627, 19.190767, -18.176437, 5.9711766, 18.392344, -19.679089, 6.777077, 17.395275, -19.968939, 7.750009, 18.392344, -19.679089, 6.777077, 17.408092, -20.237429, 7.618006, 18.392344, -19.679089, 6.777077, 19.190767, -18.176437, 5.9711766, 19.137367, -18.176437, 5.908166, 17.408092, -20.237429, 7.618006, 18.392344, -19.679089, 6.777077, 18.209078, -19.679089, 6.560063, 18.392344, -19.679089, 6.777077, 19.137367, -18.176437, 5.908166, 18.209078, -19.679089, 6.560063, 17.408092, -20.237429, 7.618006, 18.209078, -19.679089, 6.560063, 17.214787, -20.237429, 7.389458, 18.209078, -19.679089, 6.560063, 19.137367, -18.176437, 5.908166, 19.061113, -18.177078, 5.9391375, 17.214787, -20.237429, 7.389458, 18.209078, -19.679089, 6.560063, 18.027735, -19.463997, 6.5985103, 18.209078, -19.679089, 6.560063, 19.061113, -18.177078, 5.9391375, 18.027735, -19.463997, 6.5985103, 18.027735, -19.463997, 6.5985103, 19.061113, -18.177078, 5.9391375, 19.067095, -18.177505, 6.0213723, 17.214787, -20.237429, 7.389458, 18.027735, -19.463997, 6.5985103, 17.082785, -19.968939, 7.379846, 18.027735, -19.463997, 6.5985103, 19.067095, -18.177505, 6.0213723, 18.09865, -19.331139, 6.83902, 17.082785, -19.968939, 7.379846, 18.027735, -19.463997, 6.5985103, 18.09865, -19.331139, 6.83902, 17.082785, -19.968939, 7.379846, 18.09865, -19.331139, 6.83902, 17.194283, -19.802975, 7.602841, 19.916355, -18.10766, 4.50975, 18.669378, -17.361996, 4.594761, 18.997677, -18.38085, 4.7551727, 18.997677, -18.38085, 4.7551727, 18.669378, -17.361996, 4.594761, 17.938025, -17.356228, 5.7366476, 18.997677, -18.38085, 4.7551727, 17.938025, -17.356228, 5.7366476, 18.45151, -18.10766, 5.7464733, 19.916355, -18.10766, 4.50975, 19.861246, -17.356228, 4.1126738, 18.669378, -17.361996, 4.594761, 20.759848, -18.352869, 5.1768117, 19.861246, -17.356228, 4.1126738, 19.916355, -18.10766, 4.50975, 18.669378, -17.361996, 4.594761, 18.361372, -16.59625, 5.750745, 17.938025, -17.356228, 5.7366476, 20.759848, -18.352869, 5.1768117, 20.69171, -17.339567, 4.9839334, 19.861246, -17.356228, 4.1126738, 20.916626, -18.072844, 6.2943482, 20.69171, -17.339567, 4.9839334, 20.759848, -18.352869, 5.1768117, 19.861246, -17.356228, 4.1126738, 18.80138, -16.33182, 5.093936, 18.669378, -17.361996, 4.594761, 18.669378, -17.361996, 4.594761, 18.80138, -16.33182, 5.093936, 18.361372, -16.59625, 5.750745, 20.69171, -17.339567, 4.9839334, 19.58528, -16.596252, 4.7173653, 19.861246, -17.356228, 4.1126738, 19.861246, -17.356228, 4.1126738, 19.58528, -16.596252, 4.7173653, 18.80138, -16.33182, 5.093936, 20.916626, -18.072844, 6.2943482, 20.971521, -17.322693, 6.3201933, 20.69171, -17.339567, 4.9839334, 20.971521, -17.322693, 6.3201933, 20.916626, -18.072844, 6.2943482, 20.598795, -18.324886, 7.2226386, 20.69171, -17.339567, 4.9839334, 20.07292, -16.326265, 5.255842, 19.58528, -16.596252, 4.7173653, 20.971521, -17.322693, 6.3201933, 20.07292, -16.326265, 5.255842, 20.69171, -17.339567, 4.9839334, 19.58528, -16.596252, 4.7173653, 19.312944, -15.988569, 5.813969, 18.80138, -16.33182, 5.093936, 19.58528, -16.596252, 4.7173653, 20.07292, -16.326265, 5.255842, 19.312944, -15.988569, 5.813969, 20.971521, -17.322693, 6.3201933, 20.39054, -16.581512, 6.067722, 20.07292, -16.326265, 5.255842, 20.971521, -17.322693, 6.3201933, 20.598795, -18.324886, 7.2226386, 20.51122, -17.317139, 7.2331047, 20.39054, -16.581512, 6.067722, 20.971521, -17.322693, 6.3201933, 20.51122, -17.317139, 7.2331047, 20.51122, -17.317139, 7.2331047, 20.598795, -18.324886, 7.2226386, 19.692719, -18.072844, 7.327728, 20.07292, -16.326265, 5.255842, 20.39054, -16.581512, 6.067722, 19.963133, -16.320711, 6.583985, 20.39054, -16.581512, 6.067722, 20.51122, -17.317139, 7.2331047, 19.963133, -16.320711, 6.583985, 20.07292, -16.326265, 5.255842, 19.963133, -16.320711, 6.583985, 19.312944, -15.988569, 5.813969, 20.51122, -17.317139, 7.2331047, 19.692719, -18.072844, 7.327728, 19.590406, -17.322693, 7.4862165, 19.963133, -16.320711, 6.583985, 20.51122, -17.317139, 7.2331047, 19.590406, -17.322693, 7.4862165, 18.836624, -18.352867, 6.800999, 19.590406, -17.322693, 7.4862165, 19.692719, -18.072844, 7.327728, 19.312944, -15.988569, 5.813969, 19.963133, -16.320711, 6.583985, 19.407354, -16.581512, 6.8975444, 19.963133, -16.320711, 6.583985, 19.590406, -17.322693, 7.4862165, 19.407354, -16.581512, 6.8975444, 18.836624, -18.352867, 6.800999, 18.488676, -17.339567, 6.8439317, 19.590406, -17.322693, 7.4862165, 18.488676, -17.339567, 6.8439317, 19.407354, -16.581512, 6.8975444, 19.590406, -17.322693, 7.4862165, 18.45151, -18.10766, 5.7464733, 18.488676, -17.339567, 6.8439317, 18.836624, -18.352867, 6.800999, 18.45151, -18.10766, 5.7464733, 17.938025, -17.356228, 5.7366476, 18.488676, -17.339567, 6.8439317, 18.488676, -17.339567, 6.8439317, 18.691593, -16.326265, 6.4220786, 19.407354, -16.581512, 6.8975444, 19.312944, -15.988569, 5.813969, 19.407354, -16.581512, 6.8975444, 18.691593, -16.326265, 6.4220786, 17.938025, -17.356228, 5.7366476, 18.691593, -16.326265, 6.4220786, 18.488676, -17.339567, 6.8439317, 18.80138, -16.33182, 5.093936, 19.312944, -15.988569, 5.813969, 18.691593, -16.326265, 6.4220786, 17.938025, -17.356228, 5.7366476, 18.361372, -16.59625, 5.750745, 18.691593, -16.326265, 6.4220786, 18.80138, -16.33182, 5.093936, 18.691593, -16.326265, 6.4220786, 18.361372, -16.59625, 5.750745, 20.916626, -18.072844, 6.2943482, 19.867655, -18.690779, 6.0138965, 20.598795, -18.324886, 7.2226386, 20.916626, -18.072844, 6.2943482, 20.759848, -18.352869, 5.1768117, 19.867655, -18.690779, 6.0138965, 20.598795, -18.324886, 7.2226386, 19.867655, -18.690779, 6.0138965, 18.836624, -18.352867, 6.800999, 20.598795, -18.324886, 7.2226386, 18.836624, -18.352867, 6.800999, 19.692719, -18.072844, 7.327728, 19.867655, -18.690779, 6.0138965, 18.45151, -18.10766, 5.7464733, 18.836624, -18.352867, 6.800999, 19.867655, -18.690779, 6.0138965, 18.997677, -18.38085, 4.7551727, 18.45151, -18.10766, 5.7464733, 20.759848, -18.352869, 5.1768117, 18.997677, -18.38085, 4.7551727, 19.867655, -18.690779, 6.0138965, 20.759848, -18.352869, 5.1768117, 19.916355, -18.10766, 4.50975, 18.997677, -18.38085, 4.7551727, 15.432323, -17.738777, 11.597307, 17.151134, -15.819399, 12.648415, 17.029385, -18.463295, 12.383129, 17.029385, -18.463295, 12.383129, 17.151134, -15.819399, 12.648415, 19.233913, -15.752116, 11.624861, 17.029385, -18.463295, 12.383129, 19.233913, -15.752116, 11.624861, 18.862041, -17.774446, 11.419596, 15.432323, -17.738777, 11.597307, 15.174299, -15.860622, 12.197086, 17.151134, -15.819399, 12.648415, 14.790466, -18.280245, 9.459635, 15.174299, -15.860622, 12.197086, 15.432323, -17.738777, 11.597307, 17.151134, -15.819399, 12.648415, 18.632425, -13.811806, 11.039606, 19.233913, -15.752116, 11.624861, 14.790466, -18.280245, 9.459635, 14.663164, -15.672873, 9.612782, 15.174299, -15.860622, 12.197086, 15.765963, -17.546755, 7.471905, 14.663164, -15.672873, 9.612782, 14.790466, -18.280245, 9.459635, 15.174299, -15.860622, 12.197086, 17.378614, -13.149656, 11.438818, 17.151134, -15.819399, 12.648415, 17.151134, -15.819399, 12.648415, 17.378614, -13.149656, 11.438818, 18.632425, -13.811806, 11.039606, 14.663164, -15.672873, 9.612782, 16.05389, -13.860292, 11.293999, 15.174299, -15.860622, 12.197086, 15.174299, -15.860622, 12.197086, 16.05389, -13.860292, 11.293999, 17.378614, -13.149656, 11.438818, 15.765963, -17.546755, 7.471905, 15.501957, -15.53254, 7.0716248, 14.663164, -15.672873, 9.612782, 15.501957, -15.53254, 7.0716248, 15.765963, -17.546755, 7.471905, 17.558678, -18.333218, 6.7520857, 14.663164, -15.672873, 9.612782, 15.88579, -13.113132, 9.809076, 16.05389, -13.860292, 11.293999, 15.501957, -15.53254, 7.0716248, 15.88579, -13.113132, 9.809076, 14.663164, -15.672873, 9.612782, 16.05389, -13.860292, 11.293999, 17.454443, -12.259815, 9.87465, 17.378614, -13.149656, 11.438818, 16.05389, -13.860292, 11.293999, 15.88579, -13.113132, 9.809076, 17.454443, -12.259815, 9.87465, 15.501957, -15.53254, 7.0716248, 16.199562, -13.714193, 8.2419195, 15.88579, -13.113132, 9.809076, 15.501957, -15.53254, 7.0716248, 17.558678, -18.333218, 6.7520857, 17.574484, -15.715379, 6.749949, 16.199562, -13.714193, 8.2419195, 15.501957, -15.53254, 7.0716248, 17.574484, -15.715379, 6.749949, 17.574484, -15.715379, 6.749949, 17.558678, -18.333218, 6.7520857, 19.186922, -17.80478, 7.9181085, 15.88579, -13.113132, 9.809076, 16.199562, -13.714193, 8.2419195, 17.48456, -13.123599, 8.222482, 16.199562, -13.714193, 8.2419195, 17.574484, -15.715379, 6.749949, 17.48456, -13.123599, 8.222482, 15.88579, -13.113132, 9.809076, 17.48456, -13.123599, 8.222482, 17.454443, -12.259815, 9.87465, 17.574484, -15.715379, 6.749949, 19.186922, -17.80478, 7.9181085, 19.541065, -15.924276, 7.9029427, 17.48456, -13.123599, 8.222482, 17.574484, -15.715379, 6.749949, 19.541065, -15.924276, 7.9029427, 19.797594, -18.516268, 9.675581, 19.541065, -15.924276, 7.9029427, 19.186922, -17.80478, 7.9181085, 17.454443, -12.259815, 9.87465, 17.48456, -13.123599, 8.222482, 18.768915, -13.888488, 8.611227, 17.48456, -13.123599, 8.222482, 19.541065, -15.924276, 7.9029427, 18.768915, -13.888488, 8.611227, 19.797594, -18.516268, 9.675581, 20.062454, -15.861905, 9.785582, 19.541065, -15.924276, 7.9029427, 20.062454, -15.861905, 9.785582, 18.768915, -13.888488, 8.611227, 19.541065, -15.924276, 7.9029427, 18.862041, -17.774446, 11.419596, 20.062454, -15.861905, 9.785582, 19.797594, -18.516268, 9.675581, 18.862041, -17.774446, 11.419596, 19.233913, -15.752116, 11.624861, 20.062454, -15.861905, 9.785582, 20.062454, -15.861905, 9.785582, 18.977385, -13.160124, 9.852436, 18.768915, -13.888488, 8.611227, 17.454443, -12.259815, 9.87465, 18.768915, -13.888488, 8.611227, 18.977385, -13.160124, 9.852436, 19.233913, -15.752116, 11.624861, 18.977385, -13.160124, 9.852436, 20.062454, -15.861905, 9.785582, 17.378614, -13.149656, 11.438818, 17.454443, -12.259815, 9.87465, 18.977385, -13.160124, 9.852436, 19.233913, -15.752116, 11.624861, 18.632425, -13.811806, 11.039606, 18.977385, -13.160124, 9.852436, 17.378614, -13.149656, 11.438818, 18.977385, -13.160124, 9.852436, 18.632425, -13.811806, 11.039606, 15.765963, -17.546755, 7.471905, 17.271177, -19.275177, 9.523714, 17.558678, -18.333218, 6.7520857, 15.765963, -17.546755, 7.471905, 14.790466, -18.280245, 9.459635, 17.271177, -19.275177, 9.523714, 17.558678, -18.333218, 6.7520857, 17.271177, -19.275177, 9.523714, 19.797594, -18.516268, 9.675581, 17.558678, -18.333218, 6.7520857, 19.797594, -18.516268, 9.675581, 19.186922, -17.80478, 7.9181085, 17.271177, -19.275177, 9.523714, 18.862041, -17.774446, 11.419596, 19.797594, -18.516268, 9.675581, 17.271177, -19.275177, 9.523714, 17.029385, -18.463295, 12.383129, 18.862041, -17.774446, 11.419596, 14.790466, -18.280245, 9.459635, 17.029385, -18.463295, 12.383129, 17.271177, -19.275177, 9.523714, 14.790466, -18.280245, 9.459635, 15.432323, -17.738777, 11.597307, 17.029385, -18.463295, 12.383129, 6.450511, 5.6386614, 17.721281, 5.116558, 6.049283, 18.568323, 6.1998763, 5.437825, 17.553066, 6.450511, 5.6386614, 17.721281, 5.394739, 6.2101097, 18.690458, 5.116558, 6.049283, 18.568323, 5.394739, 6.2101097, 18.690458, 4.166137, 7.607539, 19.708622, 5.116558, 6.049283, 18.568323, 5.394739, 6.2101097, 18.690458, 4.2640667, 7.6070127, 19.728611, 4.166137, 7.607539, 19.708622, 5.116558, 6.049283, 18.568323, 4.166137, 7.607539, 19.708622, 4.117188, 7.608329, 19.795757, 6.1998763, 5.437825, 17.553066, 5.116558, 6.049283, 18.568323, 5.0425253, 5.7889595, 18.780462, 5.116558, 6.049283, 18.568323, 4.117188, 7.608329, 19.795757, 5.0425253, 5.7889595, 18.780462, 6.1998763, 5.437825, 17.553066, 5.0425253, 5.7889595, 18.780462, 6.190998, 5.1124864, 17.713438, 5.0425253, 5.7889595, 18.780462, 4.117188, 7.608329, 19.795757, 4.1845627, 7.608329, 19.869196, 6.190998, 5.1124864, 17.713438, 5.0425253, 5.7889595, 18.780462, 5.2750225, 5.7889595, 19.03369, 5.0425253, 5.7889595, 18.780462, 4.1845627, 7.608329, 19.869196, 5.2750225, 5.7889595, 19.03369, 6.190998, 5.1124864, 17.713438, 5.2750225, 5.7889595, 19.03369, 6.436242, 5.1124864, 17.980635, 5.2750225, 5.7889595, 19.03369, 4.1845627, 7.608329, 19.869196, 4.2755394, 7.607539, 19.827774, 6.436242, 5.1124864, 17.980635, 5.2750225, 5.7889595, 19.03369, 5.492837, 6.049283, 18.978226, 5.2750225, 5.7889595, 19.03369, 4.2755394, 7.607539, 19.827774, 5.492837, 6.049283, 18.978226, 5.492837, 6.049283, 18.978226, 4.2755394, 7.607539, 19.827774, 4.2640667, 7.6070127, 19.728611, 6.436242, 5.1124864, 17.980635, 5.492837, 6.049283, 18.978226, 6.596695, 5.437825, 17.985352, 5.492837, 6.049283, 18.978226, 4.2640667, 7.6070127, 19.728611, 5.394739, 6.2101097, 18.690458, 6.596695, 5.437825, 17.985352, 5.492837, 6.049283, 18.978226, 5.394739, 6.2101097, 18.690458, 6.596695, 5.437825, 17.985352, 5.394739, 6.2101097, 18.690458, 6.450511, 5.6386614, 17.721281, 5.7805123, 3.2207327, 16.731695, 6.2294216, 5.7555304, 17.911285, 5.4306893, 3.2207327, 17.595985, 5.7805123, 3.2207327, 16.731695, 6.351206, 5.7918544, 17.591637, 6.2294216, 5.7555304, 17.911285, 6.351206, 5.7918544, 17.591637, 6.4141273, 8.301909, 17.908752, 6.2294216, 5.7555304, 17.911285, 6.351206, 5.7918544, 17.591637, 6.4673533, 8.311122, 17.766653, 6.4141273, 8.301909, 17.908752, 6.2294216, 5.7555304, 17.911285, 6.4141273, 8.301909, 17.908752, 6.532635, 8.286643, 18.002592, 5.4306893, 3.2207327, 17.595985, 6.2294216, 5.7555304, 17.911285, 6.495885, 5.697096, 18.12049, 6.2294216, 5.7555304, 17.911285, 6.532635, 8.286643, 18.002592, 6.495885, 5.697096, 18.12049, 5.4306893, 3.2207327, 17.595985, 6.495885, 5.697096, 18.12049, 6.146731, 3.2207327, 18.143703, 6.495885, 5.697096, 18.12049, 6.532635, 8.286643, 18.002592, 6.6594005, 8.286643, 17.918467, 6.146731, 3.2207327, 18.143703, 6.495885, 5.697096, 18.12049, 6.7823133, 5.697096, 17.930405, 6.495885, 5.697096, 18.12049, 6.6594005, 8.286643, 17.918467, 6.7823133, 5.697096, 17.930405, 6.146731, 3.2207327, 18.143703, 6.7823133, 5.697096, 17.930405, 6.939343, 3.2207327, 17.617693, 6.7823133, 5.697096, 17.930405, 6.6594005, 8.286643, 17.918467, 6.6189694, 8.301909, 17.77281, 6.939343, 3.2207327, 17.617693, 6.7823133, 5.697096, 17.930405, 6.693058, 5.7555304, 17.603598, 6.7823133, 5.697096, 17.930405, 6.6189694, 8.301909, 17.77281, 6.693058, 5.7555304, 17.603598, 6.693058, 5.7555304, 17.603598, 6.6189694, 8.301909, 17.77281, 6.4673533, 8.311122, 17.766653, 6.939343, 3.2207327, 17.617693, 6.693058, 5.7555304, 17.603598, 6.712817, 3.2207322, 16.745113, 6.693058, 5.7555304, 17.603598, 6.4673533, 8.311122, 17.766653, 6.351206, 5.7918544, 17.591637, 6.712817, 3.2207322, 16.745113, 6.693058, 5.7555304, 17.603598, 6.351206, 5.7918544, 17.591637, 6.712817, 3.2207322, 16.745113, 6.351206, 5.7918544, 17.591637, 5.7805123, 3.2207327, 16.731695, 6.732394, 5.966105, 17.783781, 8.337761, 6.384096, 17.883148, 6.83874, 5.7618475, 18.072077, 6.83874, 5.7618475, 18.072077, 6.302865, 5.6723533, 17.875813, 6.732394, 5.966105, 17.783781, 6.83874, 5.7618475, 18.072077, 6.3696475, 5.543639, 18.05737, 6.302865, 5.6723533, 17.875813, 6.732394, 5.966105, 17.783781, 8.181509, 6.5478187, 17.616283, 8.337761, 6.384096, 17.883148, 8.181509, 6.5478187, 17.616283, 9.801905, 7.9697266, 17.51171, 8.337761, 6.384096, 17.883148, 8.181509, 6.5478187, 17.616283, 9.733292, 7.9692, 17.436882, 9.801905, 7.9697266, 17.51171, 8.337761, 6.384096, 17.883148, 9.801905, 7.9697266, 17.51171, 9.894417, 7.970516, 17.469269, 8.337761, 6.384096, 17.883148, 9.894417, 7.970516, 17.469269, 8.525363, 6.119035, 17.752645, 6.83874, 5.7618475, 18.072077, 8.337761, 6.384096, 17.883148, 8.525363, 6.119035, 17.752645, 8.525363, 6.119035, 17.752645, 9.894417, 7.970516, 17.469269, 9.882729, 7.970516, 17.368353, 6.9416494, 5.4307184, 17.94534, 6.3696475, 5.543639, 18.05737, 6.83874, 5.7618475, 18.072077, 6.83874, 5.7618475, 18.072077, 8.525363, 6.119035, 17.752645, 6.9416494, 5.4307184, 17.94534, 6.9416494, 5.4307184, 17.94534, 6.4345694, 5.33517, 17.977638, 6.3696475, 5.543639, 18.05737, 8.525363, 6.119035, 17.752645, 9.882729, 7.970516, 17.368353, 8.485138, 6.119035, 17.40499, 6.9416494, 5.4307184, 17.94534, 8.525363, 6.119035, 17.752645, 8.485138, 6.119035, 17.40499, 8.485138, 6.119035, 17.40499, 9.882729, 7.970516, 17.368353, 9.783121, 7.9697266, 17.348215, 6.899346, 5.4307184, 17.578844, 6.4345694, 5.33517, 17.977638, 6.9416494, 5.4307184, 17.94534, 6.9416494, 5.4307184, 17.94534, 8.485138, 6.119035, 17.40499, 6.899346, 5.4307184, 17.578844, 6.899346, 5.4307184, 17.578844, 6.4079695, 5.33517, 17.746672, 6.4345694, 5.33517, 17.977638, 8.485138, 6.119035, 17.40499, 9.783121, 7.9697266, 17.348215, 8.2728, 6.384096, 17.320663, 6.899346, 5.4307184, 17.578844, 8.485138, 6.119035, 17.40499, 8.2728, 6.384096, 17.320663, 8.2728, 6.384096, 17.320663, 9.783121, 7.9697266, 17.348215, 9.733292, 7.9692, 17.436882, 8.2728, 6.384096, 17.320663, 9.733292, 7.9692, 17.436882, 8.181509, 6.5478187, 17.616283, 6.899346, 5.4307184, 17.578844, 8.2728, 6.384096, 17.320663, 6.7699003, 5.7618475, 17.478994, 6.7699003, 5.7618475, 17.478994, 6.4079695, 5.33517, 17.746672, 6.899346, 5.4307184, 17.578844, 6.7699003, 5.7618475, 17.478994, 8.2728, 6.384096, 17.320663, 8.181509, 6.5478187, 17.616283, 6.7699003, 5.7618475, 17.478994, 6.326483, 5.543639, 17.683863, 6.4079695, 5.33517, 17.746672, 6.7699003, 5.7618475, 17.478994, 8.181509, 6.5478187, 17.616283, 6.732394, 5.966105, 17.783781, 6.732394, 5.966105, 17.783781, 6.326483, 5.543639, 17.683863, 6.7699003, 5.7618475, 17.478994, 6.732394, 5.966105, 17.783781, 6.302865, 5.6723533, 17.875813, 6.326483, 5.543639, 17.683863, 11.635334, 8.055535, 16.46335, 10.297312, 8.974432, 15.70101, 10.525072, 7.7188787, 16.089432, 10.525072, 7.7188787, 16.089432, 10.297312, 8.974432, 15.70101, 8.768291, 8.9812765, 16.37455, 10.525072, 7.7188787, 16.089432, 8.768291, 8.9812765, 16.37455, 9.2888565, 8.055536, 16.734821, 11.635334, 8.055535, 16.46335, 11.849512, 8.981276, 16.018257, 10.297312, 8.974432, 15.70101, 12.046843, 7.75336, 17.723677, 11.849512, 8.981276, 16.018257, 11.635334, 8.055535, 16.46335, 10.297312, 8.974432, 15.70101, 9.193269, 9.9178095, 16.677896, 8.768291, 8.9812765, 16.37455, 12.046843, 7.75336, 17.723677, 12.1083145, 9.00207, 17.478806, 11.849512, 8.981276, 16.018257, 11.446236, 8.09844, 18.977428, 12.1083145, 9.00207, 17.478806, 12.046843, 7.75336, 17.723677, 11.849512, 8.981276, 16.018257, 10.092733, 10.2439375, 16.303373, 10.297312, 8.974432, 15.70101, 10.297312, 8.974432, 15.70101, 10.092733, 10.2439375, 16.303373, 9.193269, 9.9178095, 16.677896, 12.1083145, 9.00207, 17.478806, 11.154192, 9.917809, 16.450953, 11.849512, 8.981276, 16.018257, 11.849512, 8.981276, 16.018257, 11.154192, 9.917809, 16.450953, 10.092733, 10.2439375, 16.303373, 11.446236, 8.09844, 18.977428, 11.4849415, 9.022602, 19.041462, 12.1083145, 9.00207, 17.478806, 11.4849415, 9.022602, 19.041462, 11.446236, 8.09844, 18.977428, 10.487244, 7.7878428, 19.713997, 12.1083145, 9.00207, 17.478806, 11.287758, 10.250781, 17.336258, 11.154192, 9.917809, 16.450953, 11.4849415, 9.022602, 19.041462, 11.287758, 10.250781, 17.336258, 12.1083145, 9.00207, 17.478806, 11.154192, 9.917809, 16.450953, 10.127245, 10.667193, 17.391205, 10.092733, 10.2439375, 16.303373, 11.154192, 9.917809, 16.450953, 11.287758, 10.250781, 17.336258, 10.127245, 10.667193, 17.391205, 11.4849415, 9.022602, 19.041462, 11.060484, 9.936234, 18.386164, 11.287758, 10.250781, 17.336258, 11.4849415, 9.022602, 19.041462, 10.487244, 7.7878428, 19.713997, 10.390107, 9.029709, 19.664736, 11.060484, 9.936234, 18.386164, 11.4849415, 9.022602, 19.041462, 10.390107, 9.029709, 19.664736, 10.390107, 9.029709, 19.664736, 10.487244, 7.7878428, 19.713997, 9.485313, 8.098441, 19.204372, 11.287758, 10.250781, 17.336258, 11.060484, 9.936234, 18.386164, 10.269789, 10.257888, 18.624687, 11.060484, 9.936234, 18.386164, 10.390107, 9.029709, 19.664736, 10.269789, 10.257888, 18.624687, 11.287758, 10.250781, 17.336258, 10.269789, 10.257888, 18.624687, 10.127245, 10.667193, 17.391205, 10.390107, 9.029709, 19.664736, 9.485313, 8.098441, 19.204372, 9.272093, 9.022602, 19.297224, 10.269789, 10.257888, 18.624687, 10.390107, 9.029709, 19.664736, 9.272093, 9.022602, 19.297224, 8.965256, 7.7533607, 18.079897, 9.272093, 9.022602, 19.297224, 9.485313, 8.098441, 19.204372, 10.127245, 10.667193, 17.391205, 10.269789, 10.257888, 18.624687, 9.485554, 9.936234, 18.568289, 10.269789, 10.257888, 18.624687, 9.272093, 9.022602, 19.297224, 9.485554, 9.936234, 18.568289, 8.965256, 7.7533607, 18.079897, 8.57874, 9.002071, 17.886866, 9.272093, 9.022602, 19.297224, 8.57874, 9.002071, 17.886866, 9.485554, 9.936234, 18.568289, 9.272093, 9.022602, 19.297224, 9.2888565, 8.055536, 16.734821, 8.57874, 9.002071, 17.886866, 8.965256, 7.7533607, 18.079897, 9.2888565, 8.055536, 16.734821, 8.768291, 8.9812765, 16.37455, 8.57874, 9.002071, 17.886866, 8.57874, 9.002071, 17.886866, 9.07469, 10.250781, 17.592167, 9.485554, 9.936234, 18.568289, 10.127245, 10.667193, 17.391205, 9.485554, 9.936234, 18.568289, 9.07469, 10.250781, 17.592167, 8.768291, 8.9812765, 16.37455, 9.07469, 10.250781, 17.592167, 8.57874, 9.002071, 17.886866, 10.092733, 10.2439375, 16.303373, 10.127245, 10.667193, 17.391205, 9.07469, 10.250781, 17.592167, 8.768291, 8.9812765, 16.37455, 9.193269, 9.9178095, 16.677896, 9.07469, 10.250781, 17.592167, 10.092733, 10.2439375, 16.303373, 9.07469, 10.250781, 17.592167, 9.193269, 9.9178095, 16.677896, 11.446236, 8.09844, 18.977428, 10.560175, 7.3369484, 17.97454, 10.487244, 7.7878428, 19.713997, 11.446236, 8.09844, 18.977428, 12.046843, 7.75336, 17.723677, 10.560175, 7.3369484, 17.97454, 10.487244, 7.7878428, 19.713997, 10.560175, 7.3369484, 17.97454, 8.965256, 7.7533607, 18.079897, 10.487244, 7.7878428, 19.713997, 8.965256, 7.7533607, 18.079897, 9.485313, 8.098441, 19.204372, 10.560175, 7.3369484, 17.97454, 9.2888565, 8.055536, 16.734821, 8.965256, 7.7533607, 18.079897, 10.560175, 7.3369484, 17.97454, 10.525072, 7.7188787, 16.089432, 9.2888565, 8.055536, 16.734821, 12.046843, 7.75336, 17.723677, 10.525072, 7.7188787, 16.089432, 10.560175, 7.3369484, 17.97454, 12.046843, 7.75336, 17.723677, 11.635334, 8.055535, 16.46335, 10.525072, 7.7188787, 16.089432, 1.4948497, 8.510117, 18.317255, 1.5868134, 10.875402, 20.798342, 1.7763877, 7.617279, 20.49278, 1.7763877, 7.617279, 20.49278, 1.5868134, 10.875402, 20.798342, 4.0570173, 10.958316, 22.239437, 1.7763877, 7.617279, 20.49278, 4.0570173, 10.958316, 22.239437, 4.0145273, 8.465897, 21.717955, 1.4948497, 8.510117, 18.317255, 0.70318604, 10.8246, 18.46102, 1.5868134, 10.875402, 20.798342, 3.2524045, 7.8428564, 16.201567, 0.70318604, 10.8246, 18.46102, 1.4948497, 8.510117, 18.317255, 1.5868134, 10.875402, 20.798342, 4.248084, 13.349396, 21.22304, 4.0570173, 10.958316, 22.239437, 3.2524045, 7.8428564, 16.201567, 3.0084083, 11.055706, 16.17521, 0.70318604, 10.8246, 18.46102, 5.9580865, 8.746749, 15.848719, 3.0084083, 11.055706, 16.17521, 3.2524045, 7.8428564, 16.201567, 0.70318604, 10.8246, 18.46102, 2.983596, 14.165375, 20.207823, 1.5868134, 10.875402, 20.798342, 1.5868134, 10.875402, 20.798342, 2.983596, 14.165375, 20.207823, 4.248084, 13.349396, 21.22304, 3.0084083, 11.055706, 16.17521, 2.2301898, 13.289908, 18.748863, 0.70318604, 10.8246, 18.46102, 0.70318604, 10.8246, 18.46102, 2.2301898, 13.289908, 18.748863, 2.983596, 14.165375, 20.207823, 5.9580865, 8.746749, 15.848719, 6.1891904, 11.228903, 15.304886, 3.0084083, 11.055706, 16.17521, 6.1891904, 11.228903, 15.304886, 5.9580865, 8.746749, 15.848719, 7.9187675, 7.777577, 17.198952, 3.0084083, 11.055706, 16.17521, 3.6397526, 14.210384, 17.564486, 2.2301898, 13.289908, 18.748863, 6.1891904, 11.228903, 15.304886, 3.6397526, 14.210384, 17.564486, 3.0084083, 11.055706, 16.17521, 2.2301898, 13.289908, 18.748863, 4.6415443, 15.261944, 19.219688, 2.983596, 14.165375, 20.207823, 2.2301898, 13.289908, 18.748863, 3.6397526, 14.210384, 17.564486, 4.6415443, 15.261944, 19.219688, 6.1891904, 11.228903, 15.304886, 5.462911, 13.469423, 16.818632, 3.6397526, 14.210384, 17.564486, 6.1891904, 11.228903, 15.304886, 7.9187675, 7.777577, 17.198952, 7.931513, 11.003588, 17.213871, 5.462911, 13.469423, 16.818632, 6.1891904, 11.228903, 15.304886, 7.931513, 11.003588, 17.213871, 7.931513, 11.003588, 17.213871, 7.9187675, 7.777577, 17.198952, 7.831026, 8.428781, 19.665352, 3.6397526, 14.210384, 17.564486, 5.462911, 13.469423, 16.818632, 6.3584847, 14.197486, 18.1248, 5.462911, 13.469423, 16.818632, 7.931513, 11.003588, 17.213871, 6.3584847, 14.197486, 18.1248, 3.6397526, 14.210384, 17.564486, 6.3584847, 14.197486, 18.1248, 4.6415443, 15.261944, 19.219688, 7.931513, 11.003588, 17.213871, 7.831026, 8.428781, 19.665352, 8.087843, 10.745896, 20.01901, 6.3584847, 14.197486, 18.1248, 7.931513, 11.003588, 17.213871, 8.087843, 10.745896, 20.01901, 6.4428244, 7.5519996, 21.489801, 8.087843, 10.745896, 20.01901, 7.831026, 8.428781, 19.665352, 4.6415443, 15.261944, 19.219688, 6.3584847, 14.197486, 18.1248, 6.8342867, 13.254899, 19.708595, 6.3584847, 14.197486, 18.1248, 8.087843, 10.745896, 20.01901, 6.8342867, 13.254899, 19.708595, 6.4428244, 7.5519996, 21.489801, 6.510137, 10.823021, 21.836857, 8.087843, 10.745896, 20.01901, 6.510137, 10.823021, 21.836857, 6.8342867, 13.254899, 19.708595, 8.087843, 10.745896, 20.01901, 4.0145273, 8.465897, 21.717955, 6.510137, 10.823021, 21.836857, 6.4428244, 7.5519996, 21.489801, 4.0145273, 8.465897, 21.717955, 4.0570173, 10.958316, 22.239437, 6.510137, 10.823021, 21.836857, 6.510137, 10.823021, 21.836857, 5.702328, 14.152213, 20.768137, 6.8342867, 13.254899, 19.708595, 4.6415443, 15.261944, 19.219688, 6.8342867, 13.254899, 19.708595, 5.702328, 14.152213, 20.768137, 4.0570173, 10.958316, 22.239437, 5.702328, 14.152213, 20.768137, 6.510137, 10.823021, 21.836857, 2.983596, 14.165375, 20.207823, 4.6415443, 15.261944, 19.219688, 5.702328, 14.152213, 20.768137, 4.0570173, 10.958316, 22.239437, 4.248084, 13.349396, 21.22304, 5.702328, 14.152213, 20.768137, 2.983596, 14.165375, 20.207823, 5.702328, 14.152213, 20.768137, 4.248084, 13.349396, 21.22304, 5.9580865, 8.746749, 15.848719, 4.877001, 6.61652, 18.79238, 7.9187675, 7.777577, 17.198952, 5.9580865, 8.746749, 15.848719, 3.2524045, 7.8428564, 16.201567, 4.877001, 6.61652, 18.79238, 7.9187675, 7.777577, 17.198952, 4.877001, 6.61652, 18.79238, 6.4428244, 7.5519996, 21.489801, 7.9187675, 7.777577, 17.198952, 6.4428244, 7.5519996, 21.489801, 7.831026, 8.428781, 19.665352, 4.877001, 6.61652, 18.79238, 4.0145273, 8.465897, 21.717955, 6.4428244, 7.5519996, 21.489801, 4.877001, 6.61652, 18.79238, 1.7763877, 7.617279, 20.49278, 4.0145273, 8.465897, 21.717955, 3.2524045, 7.8428564, 16.201567, 1.7763877, 7.617279, 20.49278, 4.877001, 6.61652, 18.79238, 3.2524045, 7.8428564, 16.201567, 1.4948497, 8.510117, 18.317255, 1.7763877, 7.617279, 20.49278, -17.758068, 7.5774293, -6.836121, -20.570257, 8.332888, -7.573155, -17.84524, 7.2079325, -7.384581, -17.758068, 7.5774293, -6.836121, -20.386482, 8.628776, -7.04528, -20.570257, 8.332888, -7.573155, -20.386482, 8.628776, -7.04528, -23.297365, 11.199757, -7.426018, -20.570257, 8.332888, -7.573155, -20.386482, 8.628776, -7.04528, -23.201662, 11.198789, -7.2690005, -23.297365, 11.199757, -7.426018, -20.570257, 8.332888, -7.573155, -23.297365, 11.199757, -7.426018, -23.476107, 11.20121, -7.3828626, -17.84524, 7.2079325, -7.384581, -20.570257, 8.332888, -7.573155, -20.948885, 7.853947, -7.4072576, -20.570257, 8.332888, -7.573155, -23.476107, 11.20121, -7.3828626, -20.948885, 7.853947, -7.4072576, -17.84524, 7.2079325, -7.384581, -20.948885, 7.853947, -7.4072576, -18.072498, 6.6093764, -7.195704, -20.948885, 7.853947, -7.4072576, -23.476107, 11.20121, -7.3828626, -23.490705, 11.20121, -7.200089, -18.072498, 6.6093764, -7.195704, -20.948885, 7.853947, -7.4072576, -20.999002, 7.853947, -6.776777, -20.948885, 7.853947, -7.4072576, -23.490705, 11.20121, -7.200089, -20.999002, 7.853947, -6.776777, -18.072498, 6.6093764, -7.195704, -20.999002, 7.853947, -6.776777, -18.12548, 6.6093764, -6.5305486, -20.999002, 7.853947, -6.776777, -23.490705, 11.20121, -7.200089, -23.320944, 11.199757, -7.129349, -18.12548, 6.6093764, -6.5305486, -20.999002, 7.853947, -6.776777, -20.651468, 8.332888, -6.5526786, -20.999002, 7.853947, -6.776777, -23.320944, 11.199757, -7.129349, -20.651468, 8.332888, -6.5526786, -20.651468, 8.332888, -6.5526786, -23.320944, 11.199757, -7.129349, -23.201662, 11.198789, -7.2690005, -18.12548, 6.6093764, -6.5305486, -20.651468, 8.332888, -6.5526786, -17.930893, 7.2079325, -6.308391, -20.651468, 8.332888, -6.5526786, -23.201662, 11.198789, -7.2690005, -20.386482, 8.628776, -7.04528, -17.930893, 7.2079325, -6.308391, -20.651468, 8.332888, -6.5526786, -20.386482, 8.628776, -7.04528, -17.930893, 7.2079325, -6.308391, -20.386482, 8.628776, -7.04528, -17.758068, 7.5774293, -6.836121, -17.263433, 3.1289406, -8.978432, -18.291029, 7.792444, -6.8961415, -18.866385, 3.1289406, -8.367504, -17.263433, 3.1289406, -8.978432, -17.707705, 7.8592725, -7.132322, -18.291029, 7.792444, -6.8961415, -17.707705, 7.8592725, -7.132322, -18.056295, 12.477256, -6.650377, -18.291029, 7.792444, -6.8961415, -17.707705, 7.8592725, -7.132322, -17.798122, 12.494205, -6.756602, -18.056295, 12.477256, -6.650377, -18.291029, 7.792444, -6.8961415, -18.056295, 12.477256, -6.650377, -18.034353, 12.449168, -6.373137, -18.866385, 3.1289406, -8.367504, -18.291029, 7.792444, -6.8961415, -18.239275, 7.6849365, -6.2750163, -18.291029, 7.792444, -6.8961415, -18.034353, 12.449168, -6.373137, -18.239275, 7.6849365, -6.2750163, -18.866385, 3.1289406, -8.367504, -18.239275, 7.6849365, -6.2750163, -18.707829, 3.1289406, -6.716517, -18.239275, 7.6849365, -6.2750163, -18.034353, 12.449168, -6.373137, -17.762217, 12.449168, -6.3076463, -18.707829, 3.1289406, -6.716517, -18.239275, 7.6849365, -6.2750163, -17.624376, 7.6849365, -6.1270385, -18.239275, 7.6849365, -6.2750163, -17.762217, 12.449168, -6.3076463, -17.624376, 7.6849365, -6.1270385, -18.707829, 3.1289406, -6.716517, -17.624376, 7.6849365, -6.1270385, -17.006262, 3.1289406, -6.3070283, -17.624376, 7.6849365, -6.1270385, -17.762217, 12.449168, -6.3076463, -17.616545, 12.477256, -6.5445485, -17.006262, 3.1289406, -6.3070283, -17.624376, 7.6849365, -6.1270385, -17.295702, 7.792444, -6.656613, -17.624376, 7.6849365, -6.1270385, -17.616545, 12.477256, -6.5445485, -17.295702, 7.792444, -6.656613, -17.295702, 7.792444, -6.656613, -17.616545, 12.477256, -6.5445485, -17.798122, 12.494205, -6.756602, -17.006262, 3.1289406, -6.3070283, -17.295702, 7.792444, -6.656613, -16.113934, 3.1289406, -7.705115, -17.295702, 7.792444, -6.656613, -17.798122, 12.494205, -6.756602, -17.707705, 7.8592725, -7.132322, -16.113934, 3.1289406, -7.705115, -17.295702, 7.792444, -6.656613, -17.707705, 7.8592725, -7.132322, -16.113934, 3.1289406, -7.705115, -17.707705, 7.8592725, -7.132322, -17.263433, 3.1289406, -8.978432, -17.48928, 8.179858, -6.3779426, -15.612703, 8.948875, -4.0898643, -17.744648, 7.804066, -5.8735633, -17.744648, 7.804066, -5.8735633, -18.151243, 7.6394153, -6.841582, -17.48928, 8.179858, -6.3779426, -17.744648, 7.804066, -5.8735633, -18.312298, 7.402608, -6.524203, -18.151243, 7.6394153, -6.841582, -17.48928, 8.179858, -6.3779426, -15.448719, 9.250092, -4.6346655, -15.612703, 8.948875, -4.0898643, -15.448719, 9.250092, -4.6346655, -13.278458, 11.866108, -2.5817409, -15.612703, 8.948875, -4.0898643, -15.448719, 9.250092, -4.6346655, -13.263538, 11.865139, -2.7679238, -13.278458, 11.866108, -2.5817409, -15.612703, 8.948875, -4.0898643, -13.278458, 11.866108, -2.5817409, -13.1054, 11.867559, -2.5102086, -15.612703, 8.948875, -4.0898643, -13.1054, 11.867559, -2.5102086, -15.201872, 8.461218, -4.00046, -17.744648, 7.804066, -5.8735633, -15.612703, 8.948875, -4.0898643, -15.201872, 8.461218, -4.00046, -15.201872, 8.461218, -4.00046, -13.1054, 11.867559, -2.5102086, -12.98403, 11.867559, -2.6523442, -17.444958, 7.1948566, -5.8935885, -18.312298, 7.402608, -6.524203, -17.744648, 7.804066, -5.8735633, -17.744648, 7.804066, -5.8735633, -15.201872, 8.461218, -4.00046, -17.444958, 7.1948566, -5.8935885, -17.444958, 7.1948566, -5.8935885, -18.123537, 7.0190673, -6.536556, -18.312298, 7.402608, -6.524203, -15.201872, 8.461218, -4.00046, -12.98403, 11.867559, -2.6523442, -14.783697, 8.461218, -4.490066, -17.444958, 7.1948566, -5.8935885, -15.201872, 8.461218, -4.00046, -14.783697, 8.461218, -4.490066, -14.783697, 8.461218, -4.490066, -12.98403, 11.867559, -2.6523442, -13.081631, 11.866107, -2.811812, -17.003992, 7.1948566, -6.4095883, -18.123537, 7.0190673, -6.536556, -17.444958, 7.1948566, -5.8935885, -17.444958, 7.1948566, -5.8935885, -14.783697, 8.461218, -4.490066, -17.003992, 7.1948566, -6.4095883, -17.003992, 7.1948566, -6.4095883, -17.845564, 7.0190673, -6.8616605, -18.123537, 7.0190673, -6.536556, -14.783697, 8.461218, -4.490066, -13.081631, 11.866107, -2.811812, -14.935968, 8.948875, -4.8818517, -17.003992, 7.1948566, -6.4095883, -14.783697, 8.461218, -4.490066, -14.935968, 8.948875, -4.8818517, -14.935968, 8.948875, -4.8818517, -13.081631, 11.866107, -2.811812, -13.263538, 11.865139, -2.7679238, -14.935968, 8.948875, -4.8818517, -13.263538, 11.865139, -2.7679238, -15.448719, 9.250092, -4.6346655, -17.003992, 7.1948566, -6.4095883, -14.935968, 8.948875, -4.8818517, -17.031534, 7.8040657, -6.7090955, -17.031534, 7.8040657, -6.7090955, -17.845564, 7.0190673, -6.8616605, -17.003992, 7.1948566, -6.4095883, -17.031534, 7.8040657, -6.7090955, -14.935968, 8.948875, -4.8818517, -15.448719, 9.250092, -4.6346655, -17.031534, 7.8040657, -6.7090955, -17.862965, 7.402608, -7.050143, -17.845564, 7.0190673, -6.8616605, -17.031534, 7.8040657, -6.7090955, -15.448719, 9.250092, -4.6346655, -17.48928, 8.179858, -6.3779426, -17.48928, 8.179858, -6.3779426, -17.862965, 7.402608, -7.050143, -17.031534, 7.8040657, -6.7090955, -17.48928, 8.179858, -6.3779426, -18.151243, 7.6394153, -6.841582, -17.862965, 7.402608, -7.050143, -9.569414, 12.023977, -1.423661, -10.217659, 13.714559, -4.1817064, -10.455915, 11.404599, -3.3882995, -10.455915, 11.404599, -3.3882995, -10.217659, 13.714559, -4.1817064, -13.040308, 13.727151, -5.3989296, -10.455915, 11.404599, -3.3882995, -13.040308, 13.727151, -5.3989296, -12.873928, 12.023979, -4.2461476, -9.569414, 12.023977, -1.423661, -8.701311, 13.727149, -1.6924205, -10.217659, 13.714559, -4.1817064, -10.752665, 11.468038, 0.7093334, -8.701311, 13.727149, -1.6924205, -9.569414, 12.023977, -1.423661, -10.217659, 13.714559, -4.1817064, -12.916918, 15.450177, -4.4462667, -13.040308, 13.727151, -5.3989296, -10.752665, 11.468038, 0.7093334, -10.345652, 13.765407, 0.48551416, -8.701311, 13.727149, -1.6924205, -13.194582, 12.102914, 1.4700174, -10.345652, 13.765407, 0.48551416, -10.752665, 11.468038, 0.7093334, -8.701311, 13.727149, -1.6924205, -11.285702, 16.050184, -3.7030501, -10.217659, 13.714559, -4.1817064, -10.217659, 13.714559, -4.1817064, -11.285702, 16.050184, -3.7030501, -12.916918, 15.450177, -4.4462667, -10.345652, 13.765407, 0.48551416, -10.15527, 15.450176, -2.0876474, -8.701311, 13.727149, -1.6924205, -8.701311, 13.727149, -1.6924205, -10.15527, 15.450176, -2.0876474, -11.285702, 16.050184, -3.7030501, -13.194582, 12.102914, 1.4700174, -13.23241, 13.80318, 1.6023726, -10.345652, 13.765407, 0.48551416, -13.23241, 13.80318, 1.6023726, -13.194582, 12.102914, 1.4700174, -15.388298, 11.531479, 1.0999866, -10.345652, 13.765407, 0.48551416, -11.1811695, 16.062777, -0.7989111, -10.15527, 15.450176, -2.0876474, -13.23241, 13.80318, 1.6023726, -11.1811695, 16.062777, -0.7989111, -10.345652, 13.765407, 0.48551416, -10.15527, 15.450176, -2.0876474, -12.708605, 16.828888, -2.2941785, -11.285702, 16.050184, -3.7030501, -10.15527, 15.450176, -2.0876474, -11.1811695, 16.062777, -0.7989111, -12.708605, 16.828888, -2.2941785, -13.23241, 13.80318, 1.6023726, -12.880806, 15.484076, 0.2096405, -11.1811695, 16.062777, -0.7989111, -13.23241, 13.80318, 1.6023726, -15.388298, 11.531479, 1.0999866, -15.443556, 13.8162565, 0.90737486, -12.880806, 15.484076, 0.2096405, -13.23241, 13.80318, 1.6023726, -15.443556, 13.8162565, 0.90737486, -15.443556, 13.8162565, 0.90737486, -15.388298, 11.531479, 1.0999866, -15.956232, 12.102915, -0.88860226, -11.1811695, 16.062777, -0.7989111, -12.880806, 15.484076, 0.2096405, -14.192514, 16.075851, -0.5573001, -12.880806, 15.484076, 0.2096405, -15.443556, 13.8162565, 0.90737486, -14.192514, 16.075851, -0.5573001, -11.1811695, 16.062777, -0.7989111, -14.192514, 16.075851, -0.5573001, -12.708605, 16.828888, -2.2941785, -15.443556, 13.8162565, 0.90737486, -15.956232, 12.102915, -0.88860226, -16.3484, 13.803182, -1.0596855, -14.192514, 16.075851, -0.5573001, -15.443556, 13.8162565, 0.90737486, -16.3484, 13.803182, -1.0596855, -15.09202, 11.4680395, -2.9977593, -16.3484, 13.803182, -1.0596855, -15.956232, 12.102915, -0.88860226, -12.708605, 16.828888, -2.2941785, -14.192514, 16.075851, -0.5573001, -15.09865, 15.4840765, -1.6848848, -14.192514, 16.075851, -0.5573001, -16.3484, 13.803182, -1.0596855, -15.09865, 15.4840765, -1.6848848, -15.09202, 11.4680395, -2.9977593, -15.31592, 13.7654085, -3.7604294, -16.3484, 13.803182, -1.0596855, -15.31592, 13.7654085, -3.7604294, -15.09865, 15.4840765, -1.6848848, -16.3484, 13.803182, -1.0596855, -12.873928, 12.023979, -4.2461476, -15.31592, 13.7654085, -3.7604294, -15.09202, 11.4680395, -2.9977593, -12.873928, 12.023979, -4.2461476, -13.040308, 13.727151, -5.3989296, -15.31592, 13.7654085, -3.7604294, -15.31592, 13.7654085, -3.7604294, -14.29763, 16.062777, -3.461082, -15.09865, 15.4840765, -1.6848848, -12.708605, 16.828888, -2.2941785, -15.09865, 15.4840765, -1.6848848, -14.29763, 16.062777, -3.461082, -13.040308, 13.727151, -5.3989296, -14.29763, 16.062777, -3.461082, -15.31592, 13.7654085, -3.7604294, -11.285702, 16.050184, -3.7030501, -12.708605, 16.828888, -2.2941785, -14.29763, 16.062777, -3.461082, -13.040308, 13.727151, -5.3989296, -12.916918, 15.450177, -4.4462667, -14.29763, 16.062777, -3.461082, -11.285702, 16.050184, -3.7030501, -14.29763, 16.062777, -3.461082, -12.916918, 15.450177, -4.4462667, -13.194582, 12.102914, 1.4700174, -12.95261, 10.701926, -0.98015237, -15.388298, 11.531479, 1.0999866, -13.194582, 12.102914, 1.4700174, -10.752665, 11.468038, 0.7093334, -12.95261, 10.701926, -0.98015237, -15.388298, 11.531479, 1.0999866, -12.95261, 10.701926, -0.98015237, -15.09202, 11.4680395, -2.9977593, -15.388298, 11.531479, 1.0999866, -15.09202, 11.4680395, -2.9977593, -15.956232, 12.102915, -0.88860226, -12.95261, 10.701926, -0.98015237, -12.873928, 12.023979, -4.2461476, -15.09202, 11.4680395, -2.9977593, -12.95261, 10.701926, -0.98015237, -10.455915, 11.404599, -3.3882995, -12.873928, 12.023979, -4.2461476, -10.752665, 11.468038, 0.7093334, -10.455915, 11.404599, -3.3882995, -12.95261, 10.701926, -0.98015237, -10.752665, 11.468038, 0.7093334, -9.569414, 12.023977, -1.423661, -10.455915, 11.404599, -3.3882995, -24.767572, 12.860314, -12.76874, -27.996283, 17.21195, -9.537579, -27.347048, 11.217676, -9.664753, -27.347048, 11.217676, -9.664753, -27.996283, 17.21195, -9.537579, -26.84493, 17.364494, -4.4035864, -27.347048, 11.217676, -9.664753, -26.84493, 17.364494, -4.4035864, -26.195316, 12.778957, -5.113938, -24.767572, 12.860314, -12.76874, -25.952778, 17.118486, -13.655661, -27.996283, 17.21195, -9.537579, -19.715057, 11.632692, -13.049604, -25.952778, 17.118486, -13.655661, -24.767572, 12.860314, -12.76874, -27.996283, 17.21195, -9.537579, -25.235796, 21.763588, -5.4189744, -26.84493, 17.364494, -4.4035864, -19.715057, 11.632692, -13.049604, -19.985107, 17.543673, -13.411457, -25.952778, 17.118486, -13.655661, -15.851015, 13.295669, -9.844912, -19.985107, 17.543673, -13.411457, -19.715057, 11.632692, -13.049604, -25.952778, 17.118486, -13.655661, -25.451134, 23.26482, -8.394608, -27.996283, 17.21195, -9.537579, -27.996283, 17.21195, -9.537579, -25.451134, 23.26482, -8.394608, -25.235796, 21.763588, -5.4189744, -19.985107, 17.543673, -13.411457, -24.42836, 21.654142, -11.237154, -25.952778, 17.118486, -13.655661, -25.952778, 17.118486, -13.655661, -24.42836, 21.654142, -11.237154, -25.451134, 23.26482, -8.394608, -15.851015, 13.295669, -9.844912, -14.828638, 17.862318, -10.214518, -19.985107, 17.543673, -13.411457, -14.828638, 17.862318, -10.214518, -15.851015, 13.295669, -9.844912, -15.215309, 11.512592, -5.5114245, -19.985107, 17.543673, -13.411457, -21.066832, 23.347628, -10.820686, -24.42836, 21.654142, -11.237154, -14.828638, 17.862318, -10.214518, -21.066832, 23.347628, -10.820686, -19.985107, 17.543673, -13.411457, -24.42836, 21.654142, -11.237154, -22.043024, 25.28228, -7.397609, -25.451134, 23.26482, -8.394608, -24.42836, 21.654142, -11.237154, -21.066832, 23.347628, -10.820686, -22.043024, 25.28228, -7.397609, -14.828638, 17.862318, -10.214518, -17.778353, 21.984411, -9.297602, -21.066832, 23.347628, -10.820686, -14.828638, 17.862318, -10.214518, -15.215309, 11.512592, -5.5114245, -15.219456, 17.447784, -5.475563, -17.778353, 21.984411, -9.297602, -14.828638, 17.862318, -10.214518, -15.219456, 17.447784, -5.475563, -15.219456, 17.447784, -5.475563, -15.215309, 11.512592, -5.5114245, -18.649284, 12.710672, -2.540853, -21.066832, 23.347628, -10.820686, -17.778353, 21.984411, -9.297602, -18.417158, 23.323898, -6.4548, -17.778353, 21.984411, -9.297602, -15.219456, 17.447784, -5.475563, -18.417158, 23.323898, -6.4548, -21.066832, 23.347628, -10.820686, -18.417158, 23.323898, -6.4548, -22.043024, 25.28228, -7.397609, -15.219456, 17.447784, -5.475563, -18.649284, 12.710672, -2.540853, -18.8043, 16.973686, -1.7518203, -18.417158, 23.323898, -6.4548, -15.219456, 17.447784, -5.475563, -18.8043, 16.973686, -1.7518203, -22.846716, 11.097576, -2.1269307, -18.8043, 16.973686, -1.7518203, -18.649284, 12.710672, -2.540853, -22.043024, 25.28228, -7.397609, -18.417158, 23.323898, -6.4548, -19.955843, 21.589733, -3.8300514, -18.417158, 23.323898, -6.4548, -18.8043, 16.973686, -1.7518203, -19.955843, 21.589733, -3.8300514, -22.846716, 11.097576, -2.1269307, -23.23016, 17.115578, -1.6015713, -18.8043, 16.973686, -1.7518203, -23.23016, 17.115578, -1.6015713, -19.955843, 21.589733, -3.8300514, -18.8043, 16.973686, -1.7518203, -26.195316, 12.778957, -5.113938, -23.23016, 17.115578, -1.6015713, -22.846716, 11.097576, -2.1269307, -26.195316, 12.778957, -5.113938, -26.84493, 17.364494, -4.4035864, -23.23016, 17.115578, -1.6015713, -23.23016, 17.115578, -1.6015713, -22.801462, 23.240604, -4.0287204, -19.955843, 21.589733, -3.8300514, -22.043024, 25.28228, -7.397609, -19.955843, 21.589733, -3.8300514, -22.801462, 23.240604, -4.0287204, -26.84493, 17.364494, -4.4035864, -22.801462, 23.240604, -4.0287204, -23.23016, 17.115578, -1.6015713, -25.451134, 23.26482, -8.394608, -22.043024, 25.28228, -7.397609, -22.801462, 23.240604, -4.0287204, -26.84493, 17.364494, -4.4035864, -25.235796, 21.763588, -5.4189744, -22.801462, 23.240604, -4.0287204, -25.451134, 23.26482, -8.394608, -22.801462, 23.240604, -4.0287204, -25.235796, 21.763588, -5.4189744, -15.851015, 13.295669, -9.844912, -21.172243, 9.376487, -7.615418, -15.215309, 11.512592, -5.5114245, -15.851015, 13.295669, -9.844912, -19.715057, 11.632692, -13.049604, -21.172243, 9.376487, -7.615418, -15.215309, 11.512592, -5.5114245, -21.172243, 9.376487, -7.615418, -22.846716, 11.097576, -2.1269307, -15.215309, 11.512592, -5.5114245, -22.846716, 11.097576, -2.1269307, -18.649284, 12.710672, -2.540853, -21.172243, 9.376487, -7.615418, -26.195316, 12.778957, -5.113938, -22.846716, 11.097576, -2.1269307, -21.172243, 9.376487, -7.615418, -27.347048, 11.217676, -9.664753, -26.195316, 12.778957, -5.113938, -19.715057, 11.632692, -13.049604, -27.347048, 11.217676, -9.664753, -21.172243, 9.376487, -7.615418, -19.715057, 11.632692, -13.049604, -24.767572, 12.860314, -12.76874, -27.347048, 11.217676, -9.664753, 15.271845, 5.6313887, -13.373863, 13.937893, 6.0420103, -12.526823, 15.02121, 5.4305525, -13.542078, 15.271845, 5.6313887, -13.373863, 14.216074, 6.202837, -12.404688, 13.937893, 6.0420103, -12.526823, 14.216074, 6.202837, -12.404688, 12.987471, 7.6002665, -11.386524, 13.937893, 6.0420103, -12.526823, 14.216074, 6.202837, -12.404688, 13.085402, 7.59974, -11.366533, 12.987471, 7.6002665, -11.386524, 13.937893, 6.0420103, -12.526823, 12.987471, 7.6002665, -11.386524, 12.938522, 7.601056, -11.299387, 15.02121, 5.4305525, -13.542078, 13.937893, 6.0420103, -12.526823, 13.863859, 5.781687, -12.314682, 13.937893, 6.0420103, -12.526823, 12.938522, 7.601056, -11.299387, 13.863859, 5.781687, -12.314682, 15.02121, 5.4305525, -13.542078, 13.863859, 5.781687, -12.314682, 15.012333, 5.1052136, -13.381706, 13.863859, 5.781687, -12.314682, 12.938522, 7.601056, -11.299387, 13.0058975, 7.601056, -11.22595, 15.012333, 5.1052136, -13.381706, 13.863859, 5.781687, -12.314682, 14.096357, 5.781687, -12.061455, 13.863859, 5.781687, -12.314682, 13.0058975, 7.601056, -11.22595, 14.096357, 5.781687, -12.061455, 15.012333, 5.1052136, -13.381706, 14.096357, 5.781687, -12.061455, 15.257576, 5.1052136, -13.11451, 14.096357, 5.781687, -12.061455, 13.0058975, 7.601056, -11.22595, 13.096874, 7.6002665, -11.26737, 15.257576, 5.1052136, -13.11451, 14.096357, 5.781687, -12.061455, 14.314171, 6.0420103, -12.116919, 14.096357, 5.781687, -12.061455, 13.096874, 7.6002665, -11.26737, 14.314171, 6.0420103, -12.116919, 14.314171, 6.0420103, -12.116919, 13.096874, 7.6002665, -11.26737, 13.085402, 7.59974, -11.366533, 15.257576, 5.1052136, -13.11451, 14.314171, 6.0420103, -12.116919, 15.41803, 5.4305525, -13.109793, 14.314171, 6.0420103, -12.116919, 13.085402, 7.59974, -11.366533, 14.216074, 6.202837, -12.404688, 15.41803, 5.4305525, -13.109793, 14.314171, 6.0420103, -12.116919, 14.216074, 6.202837, -12.404688, 15.41803, 5.4305525, -13.109793, 14.216074, 6.202837, -12.404688, 15.271845, 5.6313887, -13.373863, 14.601847, 3.21346, -14.363449, 15.050756, 5.7482576, -13.183859, 14.252024, 3.21346, -13.499159, 14.601847, 3.21346, -14.363449, 15.172541, 5.7845817, -13.503508, 15.050756, 5.7482576, -13.183859, 15.172541, 5.7845817, -13.503508, 15.235462, 8.294637, -13.186392, 15.050756, 5.7482576, -13.183859, 15.172541, 5.7845817, -13.503508, 15.288689, 8.303849, -13.328491, 15.235462, 8.294637, -13.186392, 15.050756, 5.7482576, -13.183859, 15.235462, 8.294637, -13.186392, 15.35397, 8.27937, -13.092552, 14.252024, 3.21346, -13.499159, 15.050756, 5.7482576, -13.183859, 15.317219, 5.689823, -12.974655, 15.050756, 5.7482576, -13.183859, 15.35397, 8.27937, -13.092552, 15.317219, 5.689823, -12.974655, 14.252024, 3.21346, -13.499159, 15.317219, 5.689823, -12.974655, 14.968065, 3.21346, -12.951441, 15.317219, 5.689823, -12.974655, 15.35397, 8.27937, -13.092552, 15.480734, 8.27937, -13.17668, 14.968065, 3.21346, -12.951441, 15.317219, 5.689823, -12.974655, 15.603647, 5.689823, -13.16474, 15.317219, 5.689823, -12.974655, 15.480734, 8.27937, -13.17668, 15.603647, 5.689823, -13.16474, 14.968065, 3.21346, -12.951441, 15.603647, 5.689823, -13.16474, 15.760677, 3.21346, -13.477451, 15.603647, 5.689823, -13.16474, 15.480734, 8.27937, -13.17668, 15.440304, 8.294637, -13.322334, 15.760677, 3.21346, -13.477451, 15.603647, 5.689823, -13.16474, 15.514393, 5.7482576, -13.491547, 15.603647, 5.689823, -13.16474, 15.440304, 8.294637, -13.322334, 15.514393, 5.7482576, -13.491547, 15.514393, 5.7482576, -13.491547, 15.440304, 8.294637, -13.322334, 15.288689, 8.303849, -13.328491, 15.760677, 3.21346, -13.477451, 15.514393, 5.7482576, -13.491547, 15.534151, 3.2134595, -14.350031, 15.514393, 5.7482576, -13.491547, 15.288689, 8.303849, -13.328491, 15.172541, 5.7845817, -13.503508, 15.534151, 3.2134595, -14.350031, 15.514393, 5.7482576, -13.491547, 15.172541, 5.7845817, -13.503508, 15.534151, 3.2134595, -14.350031, 15.172541, 5.7845817, -13.503508, 14.601847, 3.21346, -14.363449, 15.553728, 5.9588323, -13.311363, 17.159096, 6.3768234, -13.211996, 15.660074, 5.754575, -13.023067, 15.660074, 5.754575, -13.023067, 15.124199, 5.6650805, -13.219332, 15.553728, 5.9588323, -13.311363, 15.660074, 5.754575, -13.023067, 15.190983, 5.5363665, -13.037777, 15.124199, 5.6650805, -13.219332, 15.553728, 5.9588323, -13.311363, 17.002844, 6.540546, -13.478861, 17.159096, 6.3768234, -13.211996, 17.002844, 6.540546, -13.478861, 18.62324, 7.962454, -13.583435, 17.159096, 6.3768234, -13.211996, 17.002844, 6.540546, -13.478861, 18.554626, 7.9619274, -13.658262, 18.62324, 7.962454, -13.583435, 17.159096, 6.3768234, -13.211996, 18.62324, 7.962454, -13.583435, 18.715752, 7.9632435, -13.625875, 17.159096, 6.3768234, -13.211996, 18.715752, 7.9632435, -13.625875, 17.346697, 6.111762, -13.342499, 15.660074, 5.754575, -13.023067, 17.159096, 6.3768234, -13.211996, 17.346697, 6.111762, -13.342499, 17.346697, 6.111762, -13.342499, 18.715752, 7.9632435, -13.625875, 18.704063, 7.9632435, -13.726791, 15.762983, 5.4234457, -13.149805, 15.190983, 5.5363665, -13.037777, 15.660074, 5.754575, -13.023067, 15.660074, 5.754575, -13.023067, 17.346697, 6.111762, -13.342499, 15.762983, 5.4234457, -13.149805, 15.762983, 5.4234457, -13.149805, 15.255903, 5.327897, -13.117506, 15.190983, 5.5363665, -13.037777, 17.346697, 6.111762, -13.342499, 18.704063, 7.9632435, -13.726791, 17.30647, 6.111762, -13.690155, 15.762983, 5.4234457, -13.149805, 17.346697, 6.111762, -13.342499, 17.30647, 6.111762, -13.690155, 17.30647, 6.111762, -13.690155, 18.704063, 7.9632435, -13.726791, 18.604456, 7.962454, -13.746929, 15.72068, 5.4234457, -13.5163, 15.255903, 5.327897, -13.117506, 15.762983, 5.4234457, -13.149805, 15.762983, 5.4234457, -13.149805, 17.30647, 6.111762, -13.690155, 15.72068, 5.4234457, -13.5163, 15.72068, 5.4234457, -13.5163, 15.229303, 5.327897, -13.348473, 15.255903, 5.327897, -13.117506, 17.30647, 6.111762, -13.690155, 18.604456, 7.962454, -13.746929, 17.094133, 6.3768234, -13.774481, 15.72068, 5.4234457, -13.5163, 17.30647, 6.111762, -13.690155, 17.094133, 6.3768234, -13.774481, 17.094133, 6.3768234, -13.774481, 18.604456, 7.962454, -13.746929, 18.554626, 7.9619274, -13.658262, 17.094133, 6.3768234, -13.774481, 18.554626, 7.9619274, -13.658262, 17.002844, 6.540546, -13.478861, 15.72068, 5.4234457, -13.5163, 17.094133, 6.3768234, -13.774481, 15.591234, 5.754575, -13.61615, 15.591234, 5.754575, -13.61615, 15.229303, 5.327897, -13.348473, 15.72068, 5.4234457, -13.5163, 15.591234, 5.754575, -13.61615, 17.094133, 6.3768234, -13.774481, 17.002844, 6.540546, -13.478861, 15.591234, 5.754575, -13.61615, 15.147818, 5.5363665, -13.411282, 15.229303, 5.327897, -13.348473, 15.591234, 5.754575, -13.61615, 17.002844, 6.540546, -13.478861, 15.553728, 5.9588323, -13.311363, 15.553728, 5.9588323, -13.311363, 15.147818, 5.5363665, -13.411282, 15.591234, 5.754575, -13.61615, 15.553728, 5.9588323, -13.311363, 15.124199, 5.6650805, -13.219332, 15.147818, 5.5363665, -13.411282, 20.456669, 8.048263, -14.631794, 19.118647, 8.967159, -15.3941345, 19.346407, 7.711606, -15.0057125, 19.346407, 7.711606, -15.0057125, 19.118647, 8.967159, -15.3941345, 17.589626, 8.974004, -14.720594, 19.346407, 7.711606, -15.0057125, 17.589626, 8.974004, -14.720594, 18.110191, 8.048264, -14.360323, 20.456669, 8.048263, -14.631794, 20.670847, 8.974003, -15.076887, 19.118647, 8.967159, -15.3941345, 20.868176, 7.746087, -13.371468, 20.670847, 8.974003, -15.076887, 20.456669, 8.048263, -14.631794, 19.118647, 8.967159, -15.3941345, 18.014603, 9.910537, -14.417248, 17.589626, 8.974004, -14.720594, 20.868176, 7.746087, -13.371468, 20.929647, 8.994798, -13.616339, 20.670847, 8.974003, -15.076887, 20.26757, 8.091167, -12.117716, 20.929647, 8.994798, -13.616339, 20.868176, 7.746087, -13.371468, 20.670847, 8.974003, -15.076887, 18.914066, 10.236665, -14.791771, 19.118647, 8.967159, -15.3941345, 19.118647, 8.967159, -15.3941345, 18.914066, 10.236665, -14.791771, 18.014603, 9.910537, -14.417248, 20.929647, 8.994798, -13.616339, 19.975527, 9.910536, -14.644192, 20.670847, 8.974003, -15.076887, 20.670847, 8.974003, -15.076887, 19.975527, 9.910536, -14.644192, 18.914066, 10.236665, -14.791771, 20.26757, 8.091167, -12.117716, 20.306274, 9.015329, -12.053684, 20.929647, 8.994798, -13.616339, 20.306274, 9.015329, -12.053684, 20.26757, 8.091167, -12.117716, 19.308578, 7.78057, -11.381147, 20.929647, 8.994798, -13.616339, 20.10909, 10.243508, -13.758886, 19.975527, 9.910536, -14.644192, 20.306274, 9.015329, -12.053684, 20.10909, 10.243508, -13.758886, 20.929647, 8.994798, -13.616339, 19.975527, 9.910536, -14.644192, 18.948578, 10.659921, -13.703939, 18.914066, 10.236665, -14.791771, 19.975527, 9.910536, -14.644192, 20.10909, 10.243508, -13.758886, 18.948578, 10.659921, -13.703939, 20.306274, 9.015329, -12.053684, 19.881817, 9.928962, -12.708981, 20.10909, 10.243508, -13.758886, 20.306274, 9.015329, -12.053684, 19.308578, 7.78057, -11.381147, 19.211441, 9.022436, -11.43041, 19.881817, 9.928962, -12.708981, 20.306274, 9.015329, -12.053684, 19.211441, 9.022436, -11.43041, 19.211441, 9.022436, -11.43041, 19.308578, 7.78057, -11.381147, 18.306646, 8.091168, -11.890772, 20.10909, 10.243508, -13.758886, 19.881817, 9.928962, -12.708981, 19.091122, 10.250615, -12.470457, 19.881817, 9.928962, -12.708981, 19.211441, 9.022436, -11.43041, 19.091122, 10.250615, -12.470457, 20.10909, 10.243508, -13.758886, 19.091122, 10.250615, -12.470457, 18.948578, 10.659921, -13.703939, 19.211441, 9.022436, -11.43041, 18.306646, 8.091168, -11.890772, 18.093428, 9.015329, -11.79792, 19.091122, 10.250615, -12.470457, 19.211441, 9.022436, -11.43041, 18.093428, 9.015329, -11.79792, 17.78659, 7.746088, -13.015247, 18.093428, 9.015329, -11.79792, 18.306646, 8.091168, -11.890772, 18.948578, 10.659921, -13.703939, 19.091122, 10.250615, -12.470457, 18.306887, 9.928962, -12.526855, 19.091122, 10.250615, -12.470457, 18.093428, 9.015329, -11.79792, 18.306887, 9.928962, -12.526855, 17.78659, 7.746088, -13.015247, 17.400074, 8.994799, -13.208279, 18.093428, 9.015329, -11.79792, 17.400074, 8.994799, -13.208279, 18.306887, 9.928962, -12.526855, 18.093428, 9.015329, -11.79792, 18.110191, 8.048264, -14.360323, 17.400074, 8.994799, -13.208279, 17.78659, 7.746088, -13.015247, 18.110191, 8.048264, -14.360323, 17.589626, 8.974004, -14.720594, 17.400074, 8.994799, -13.208279, 17.400074, 8.994799, -13.208279, 17.896025, 10.243508, -13.502977, 18.306887, 9.928962, -12.526855, 18.948578, 10.659921, -13.703939, 18.306887, 9.928962, -12.526855, 17.896025, 10.243508, -13.502977, 17.589626, 8.974004, -14.720594, 17.896025, 10.243508, -13.502977, 17.400074, 8.994799, -13.208279, 18.914066, 10.236665, -14.791771, 18.948578, 10.659921, -13.703939, 17.896025, 10.243508, -13.502977, 17.589626, 8.974004, -14.720594, 18.014603, 9.910537, -14.417248, 17.896025, 10.243508, -13.502977, 18.914066, 10.236665, -14.791771, 17.896025, 10.243508, -13.502977, 18.014603, 9.910537, -14.417248, 20.26757, 8.091167, -12.117716, 19.381508, 7.3296757, -13.120605, 19.308578, 7.78057, -11.381147, 20.26757, 8.091167, -12.117716, 20.868176, 7.746087, -13.371468, 19.381508, 7.3296757, -13.120605, 19.308578, 7.78057, -11.381147, 19.381508, 7.3296757, -13.120605, 17.78659, 7.746088, -13.015247, 19.308578, 7.78057, -11.381147, 17.78659, 7.746088, -13.015247, 18.306646, 8.091168, -11.890772, 19.381508, 7.3296757, -13.120605, 18.110191, 8.048264, -14.360323, 17.78659, 7.746088, -13.015247, 19.381508, 7.3296757, -13.120605, 19.346407, 7.711606, -15.0057125, 18.110191, 8.048264, -14.360323, 20.868176, 7.746087, -13.371468, 19.346407, 7.711606, -15.0057125, 19.381508, 7.3296757, -13.120605, 20.868176, 7.746087, -13.371468, 20.456669, 8.048263, -14.631794, 19.346407, 7.711606, -15.0057125, 10.316184, 8.502844, -12.777889, 10.408148, 10.86813, -10.2968025, 10.597721, 7.6100063, -10.6023655, 10.597721, 7.6100063, -10.6023655, 10.408148, 10.86813, -10.2968025, 12.878351, 10.951043, -8.855707, 10.597721, 7.6100063, -10.6023655, 12.878351, 10.951043, -8.855707, 12.835861, 8.458624, -9.37719, 10.316184, 8.502844, -12.777889, 9.524521, 10.8173275, -12.634127, 10.408148, 10.86813, -10.2968025, 12.073738, 7.8355837, -14.893578, 9.524521, 10.8173275, -12.634127, 10.316184, 8.502844, -12.777889, 10.408148, 10.86813, -10.2968025, 13.069418, 13.342123, -9.872105, 12.878351, 10.951043, -8.855707, 12.073738, 7.8355837, -14.893578, 11.829742, 11.048433, -14.919933, 9.524521, 10.8173275, -12.634127, 14.779421, 8.739476, -15.246426, 11.829742, 11.048433, -14.919933, 12.073738, 7.8355837, -14.893578, 9.524521, 10.8173275, -12.634127, 11.80493, 14.158102, -10.887323, 10.408148, 10.86813, -10.2968025, 10.408148, 10.86813, -10.2968025, 11.80493, 14.158102, -10.887323, 13.069418, 13.342123, -9.872105, 11.829742, 11.048433, -14.919933, 11.051523, 13.282636, -12.346281, 9.524521, 10.8173275, -12.634127, 9.524521, 10.8173275, -12.634127, 11.051523, 13.282636, -12.346281, 11.80493, 14.158102, -10.887323, 14.779421, 8.739476, -15.246426, 15.010525, 11.22163, -15.7902565, 11.829742, 11.048433, -14.919933, 15.010525, 11.22163, -15.7902565, 14.779421, 8.739476, -15.246426, 16.7401, 7.770304, -13.896193, 11.829742, 11.048433, -14.919933, 12.461086, 14.203112, -13.530659, 11.051523, 13.282636, -12.346281, 15.010525, 11.22163, -15.7902565, 12.461086, 14.203112, -13.530659, 11.829742, 11.048433, -14.919933, 11.051523, 13.282636, -12.346281, 13.462879, 15.254671, -11.875458, 11.80493, 14.158102, -10.887323, 11.051523, 13.282636, -12.346281, 12.461086, 14.203112, -13.530659, 13.462879, 15.254671, -11.875458, 15.010525, 11.22163, -15.7902565, 14.284246, 13.462151, -14.276512, 12.461086, 14.203112, -13.530659, 15.010525, 11.22163, -15.7902565, 16.7401, 7.770304, -13.896193, 16.752848, 10.996315, -13.881273, 14.284246, 13.462151, -14.276512, 15.010525, 11.22163, -15.7902565, 16.752848, 10.996315, -13.881273, 16.752848, 10.996315, -13.881273, 16.7401, 7.770304, -13.896193, 16.65236, 8.421508, -11.429792, 12.461086, 14.203112, -13.530659, 14.284246, 13.462151, -14.276512, 15.179819, 14.190213, -12.970345, 14.284246, 13.462151, -14.276512, 16.752848, 10.996315, -13.881273, 15.179819, 14.190213, -12.970345, 12.461086, 14.203112, -13.530659, 15.179819, 14.190213, -12.970345, 13.462879, 15.254671, -11.875458, 16.752848, 10.996315, -13.881273, 16.65236, 8.421508, -11.429792, 16.909176, 10.738624, -11.076134, 15.179819, 14.190213, -12.970345, 16.752848, 10.996315, -13.881273, 16.909176, 10.738624, -11.076134, 15.264158, 7.544727, -9.605343, 16.909176, 10.738624, -11.076134, 16.65236, 8.421508, -11.429792, 13.462879, 15.254671, -11.875458, 15.179819, 14.190213, -12.970345, 15.655621, 13.247626, -11.386549, 15.179819, 14.190213, -12.970345, 16.909176, 10.738624, -11.076134, 15.655621, 13.247626, -11.386549, 15.264158, 7.544727, -9.605343, 15.3314705, 10.815748, -9.258287, 16.909176, 10.738624, -11.076134, 15.3314705, 10.815748, -9.258287, 15.655621, 13.247626, -11.386549, 16.909176, 10.738624, -11.076134, 12.835861, 8.458624, -9.37719, 15.3314705, 10.815748, -9.258287, 15.264158, 7.544727, -9.605343, 12.835861, 8.458624, -9.37719, 12.878351, 10.951043, -8.855707, 15.3314705, 10.815748, -9.258287, 15.3314705, 10.815748, -9.258287, 14.523663, 14.14494, -10.327007, 15.655621, 13.247626, -11.386549, 13.462879, 15.254671, -11.875458, 15.655621, 13.247626, -11.386549, 14.523663, 14.14494, -10.327007, 12.878351, 10.951043, -8.855707, 14.523663, 14.14494, -10.327007, 15.3314705, 10.815748, -9.258287, 11.80493, 14.158102, -10.887323, 13.462879, 15.254671, -11.875458, 14.523663, 14.14494, -10.327007, 12.878351, 10.951043, -8.855707, 13.069418, 13.342123, -9.872105, 14.523663, 14.14494, -10.327007, 11.80493, 14.158102, -10.887323, 14.523663, 14.14494, -10.327007, 13.069418, 13.342123, -9.872105, 14.779421, 8.739476, -15.246426, 13.698336, 6.609247, -12.302765, 16.7401, 7.770304, -13.896193, 14.779421, 8.739476, -15.246426, 12.073738, 7.8355837, -14.893578, 13.698336, 6.609247, -12.302765, 16.7401, 7.770304, -13.896193, 13.698336, 6.609247, -12.302765, 15.264158, 7.544727, -9.605343, 16.7401, 7.770304, -13.896193, 15.264158, 7.544727, -9.605343, 16.65236, 8.421508, -11.429792, 13.698336, 6.609247, -12.302765, 12.835861, 8.458624, -9.37719, 15.264158, 7.544727, -9.605343, 13.698336, 6.609247, -12.302765, 10.597721, 7.6100063, -10.6023655, 12.835861, 8.458624, -9.37719, 12.073738, 7.8355837, -14.893578, 10.597721, 7.6100063, -10.6023655, 13.698336, 6.609247, -12.302765, 12.073738, 7.8355837, -14.893578, 10.316184, 8.502844, -12.777889, 10.597721, 7.6100063, -10.6023655, 0.9335377, -18.661976, -2.173924, 1.8889831, -18.823338, -0.58916974, 1.5114222, -18.399822, -1.4170885, 0.9335377, -18.661976, -2.173924, 0.9335377, -18.661974, 1.0975094, 1.8889831, -18.823338, -0.58916974, 1.8889831, -18.823338, -0.58916974, 0.9335377, -18.661974, 1.0975094, 1.6508096, -18.476696, 0.3862325, -0.9279604, -18.475744, -2.1898937, 0.9335377, -18.661976, -2.173924, -0.001098156, -18.845234, -2.413155, -1.7685199, -18.399822, -0.5382087, 0.9335377, -18.661976, -2.173924, -0.9279604, -18.475744, -2.1898937, 0.9335377, -18.661974, 1.0975094, 0.9335377, -18.661976, -2.173924, -1.7685199, -18.399822, -0.5382087, -1.7685199, -18.399822, -0.5382087, -0.9279604, -18.475744, -2.1898937, -1.6464877, -18.661976, -1.4825313, 0.9335377, -18.661974, 1.0975094, -1.7685199, -18.399822, -0.5382087, -0.95520806, -18.661974, 1.0975094, -0.95520806, -18.661974, 1.0975094, -1.7685199, -18.399822, -0.5382087, -1.6464877, -18.661974, 0.40619445, 0.9335377, -18.661974, 1.0975094, -0.95520806, -18.661974, 1.0975094, -0.010835171, -18.399822, 1.2195511, 1.5114222, -18.399822, -1.4170885, -0.010835171, -14.884386, -0.5382099, 0.9335377, -18.661976, -2.173924, 0.9335377, -18.661976, -2.173924, -0.010835171, -14.884386, -0.5382099, -0.001098156, -18.845234, -2.413155, -0.001098156, -18.845234, -2.413155, -0.010835171, -14.884386, -0.5382099, -0.9279604, -18.475744, -2.1898937, -0.9279604, -18.475744, -2.1898937, -0.010835171, -14.884386, -0.5382099, -1.6464877, -18.661976, -1.4825313, -1.6464877, -18.661976, -1.4825313, -0.010835171, -14.884386, -0.5382099, -1.7685199, -18.399822, -0.5382087, -1.7685199, -18.399822, -0.5382087, -0.010835171, -14.884386, -0.5382099, -1.6464877, -18.661974, 0.40619445, -1.6464877, -18.661974, 0.40619445, -0.010835171, -14.884386, -0.5382099, -0.95520806, -18.661974, 1.0975094, -0.95520806, -18.661974, 1.0975094, -0.010835171, -14.884386, -0.5382099, -0.010835171, -18.399822, 1.2195511, -0.010835171, -18.399822, 1.2195511, -0.010835171, -14.884386, -0.5382099, 0.9335377, -18.661974, 1.0975094, 0.9335377, -18.661974, 1.0975094, -0.010835171, -14.884386, -0.5382099, 1.6508096, -18.476696, 0.3862325, 1.6508096, -18.476696, 0.3862325, -0.010835171, -14.884386, -0.5382099, 1.8889831, -18.823338, -0.58916974, 1.8889831, -18.823338, -0.58916974, -0.010835171, -14.884386, -0.5382099, 1.5114222, -18.399822, -1.4170885, 0.79053855, -17.29707, -2.0162632, 1.2353985, -16.7235, -0.5382092, 1.198884, -17.024681, -1.2366474, 0.79053855, -17.29707, -2.0162632, 0.4979446, -16.83131, 0.34912467, 1.2353985, -16.7235, -0.5382092, 1.2353985, -16.7235, -0.5382092, 0.4979446, -16.83131, 0.34912467, 1.198884, -17.024681, 0.16022909, 0.4979446, -16.83131, 0.34912467, -0.88492775, -17.22686, 1.0175039, -0.010835171, -17.024681, 0.8586674, 0.4979446, -16.83131, 0.34912467, -1.2205544, -17.024681, 0.16022909, -0.88492775, -17.22686, 1.0175039, 0.79053855, -17.29707, -2.0162632, -1.2205544, -17.024681, 0.16022909, 0.4979446, -16.83131, 0.34912467, -1.2205544, -17.024681, 0.16022909, 0.79053855, -17.29707, -2.0162632, -0.6996014, -16.880219, -1.4757214, -0.6996014, -16.880219, -1.4757214, 0.79053855, -17.29707, -2.0162632, -0.010835171, -17.024681, -1.9350855, -1.2205544, -17.024681, 0.16022909, -0.6996014, -16.880219, -1.4757214, -1.2205544, -17.024681, -1.2366474, -1.2205544, -17.024681, 0.16022909, -1.2205544, -17.024681, -1.2366474, -1.5400834, -17.234833, -0.5375037, 1.198884, -17.024681, -1.2366474, -0.010835171, -14.231086, -0.53821015, 0.79053855, -17.29707, -2.0162632, 0.79053855, -17.29707, -2.0162632, -0.010835171, -14.231086, -0.53821015, -0.010835171, -17.024681, -1.9350855, -0.010835171, -17.024681, -1.9350855, -0.010835171, -14.231086, -0.53821015, -0.6996014, -16.880219, -1.4757214, -0.6996014, -16.880219, -1.4757214, -0.010835171, -14.231086, -0.53821015, -1.2205544, -17.024681, -1.2366474, -1.2205544, -17.024681, -1.2366474, -0.010835171, -14.231086, -0.53821015, -1.5400834, -17.234833, -0.5375037, -1.5400834, -17.234833, -0.5375037, -0.010835171, -14.231086, -0.53821015, -1.2205544, -17.024681, 0.16022909, -1.2205544, -17.024681, 0.16022909, -0.010835171, -14.231086, -0.53821015, -0.88492775, -17.22686, 1.0175039, -0.88492775, -17.22686, 1.0175039, -0.010835171, -14.231086, -0.53821015, -0.010835171, -17.024681, 0.8586674, -0.010835171, -17.024681, 0.8586674, -0.010835171, -14.231086, -0.53821015, 0.4979446, -16.83131, 0.34912467, 0.4979446, -16.83131, 0.34912467, -0.010835171, -14.231086, -0.53821015, 1.198884, -17.024681, 0.16022909, 1.198884, -17.024681, 0.16022909, -0.010835171, -14.231086, -0.53821015, 1.2353985, -16.7235, -0.5382092, 1.2353985, -16.7235, -0.5382092, -0.010835171, -14.231086, -0.53821015, 1.198884, -17.024681, -1.2366474, 1.3094504, -20.552734, -2.7694182, 2.5816762, -20.552734, -0.51041746, 2.2008963, -20.875694, -1.8151531, 1.3094504, -20.552734, -2.7694182, 1.2661028, -20.875694, 1.6735137, 2.5816762, -20.552734, -0.51041746, 2.5816762, -20.552734, -0.51041746, 1.2661028, -20.875694, 1.6735137, 2.2298722, -20.624132, 0.7323996, -1.2877731, -20.875696, -2.7499266, 1.3094504, -20.552734, -2.7694182, -0.039105892, -21.061094, -3.1164474, -2.5646338, -20.875694, -0.53820777, 1.3094504, -20.552734, -2.7694182, -1.2877731, -20.875696, -2.7499266, 1.2661028, -20.875694, 1.6735137, 1.3094504, -20.552734, -2.7694182, -2.5646338, -20.875694, -0.53820777, -2.5646338, -20.875694, -0.53820777, -1.2877731, -20.875696, -2.7499266, -2.1870723, -20.562729, -1.6952248, 1.2661028, -20.875694, 1.6735137, -2.5646338, -20.875694, -0.53820777, -1.2877731, -20.875694, 1.6735137, -1.2877731, -20.875694, 1.6735137, -2.5646338, -20.875694, -0.53820777, -2.1694045, -20.59831, 0.7562758, 1.2661028, -20.875694, 1.6735137, -1.2877731, -20.875694, 1.6735137, -0.033530712, -21.08287, 2.0138073, 2.2008963, -20.875694, -1.8151531, -0.010835171, -15.767945, -0.5382097, 1.3094504, -20.552734, -2.7694182, 1.3094504, -20.552734, -2.7694182, -0.010835171, -15.767945, -0.5382097, -0.039105892, -21.061094, -3.1164474, -0.039105892, -21.061094, -3.1164474, -0.010835171, -15.767945, -0.5382097, -1.2877731, -20.875696, -2.7499266, -1.2877731, -20.875696, -2.7499266, -0.010835171, -15.767945, -0.5382097, -2.1870723, -20.562729, -1.6952248, -2.1870723, -20.562729, -1.6952248, -0.010835171, -15.767945, -0.5382097, -2.5646338, -20.875694, -0.53820777, -2.5646338, -20.875694, -0.53820777, -0.010835171, -15.767945, -0.5382097, -2.1694045, -20.59831, 0.7562758, -2.1694045, -20.59831, 0.7562758, -0.010835171, -15.767945, -0.5382097, -1.2877731, -20.875694, 1.6735137, -1.2877731, -20.875694, 1.6735137, -0.010835171, -15.767945, -0.5382097, -0.033530712, -21.08287, 2.0138073, -0.033530712, -21.08287, 2.0138073, -0.010835171, -15.767945, -0.5382097, 1.2661028, -20.875694, 1.6735137, 1.2661028, -20.875694, 1.6735137, -0.010835171, -15.767945, -0.5382097, 2.2298722, -20.624132, 0.7323996, 2.2298722, -20.624132, 0.7323996, -0.010835171, -15.767945, -0.5382097, 2.5816762, -20.552734, -0.51041746, 2.5816762, -20.552734, -0.51041746, -0.010835171, -15.767945, -0.5382097, 2.2008963, -20.875694, -1.8151531, 0.5583327, -21.275528, -0.82769513, 0.37442207, -20.354485, -1.1662683, 0.31780314, -21.275528, -1.0682597, 0.5583327, -21.275528, -0.82769513, 0.65641344, -20.354485, -0.8842943, 0.37442207, -20.354485, -1.1662683, 0.6890812, -22.029617, -0.9031601, 0.5583327, -21.275528, -0.82769513, 0.31780314, -21.275528, -1.0682597, 0.6890812, -22.029617, -0.9031601, 0.31780314, -21.275528, -1.0682597, 0.39326835, -22.029617, -1.1989906, 0.31780314, -21.275528, -1.0682597, -0.010835171, -20.354485, -1.2695241, -0.010835171, -21.275528, -1.1563258, 0.31780314, -21.275528, -1.0682597, 0.37442207, -20.354485, -1.1662683, -0.010835171, -20.354485, -1.2695241, 0.39326835, -22.029617, -1.1989906, 0.31780314, -21.275528, -1.0682597, -0.010835171, -21.275528, -1.1563258, 0.39326835, -22.029617, -1.1989906, -0.010835171, -21.275528, -1.1563258, -0.010835171, -22.029617, -1.307256, -0.010835171, -21.275528, -1.1563258, -0.3960147, -20.354485, -1.1662683, -0.339396, -21.275528, -1.0682597, -0.010835171, -21.275528, -1.1563258, -0.010835171, -20.354485, -1.2695241, -0.3960147, -20.354485, -1.1662683, -0.010835171, -22.029617, -1.307256, -0.010835171, -21.275528, -1.1563258, -0.339396, -21.275528, -1.0682597, -0.010835171, -22.029617, -1.307256, -0.339396, -21.275528, -1.0682597, -0.4148612, -22.029617, -1.1989906, -0.339396, -21.275528, -1.0682597, -0.6780064, -20.354485, -0.8842943, -0.580003, -21.275528, -0.82769513, -0.339396, -21.275528, -1.0682597, -0.3960147, -20.354485, -1.1662683, -0.6780064, -20.354485, -0.8842943, -0.4148612, -22.029617, -1.1989906, -0.339396, -21.275528, -1.0682597, -0.580003, -21.275528, -0.82769513, -0.4148612, -22.029617, -1.1989906, -0.580003, -21.275528, -0.82769513, -0.71067405, -22.029617, -0.9031601, -0.580003, -21.275528, -0.82769513, -0.78126955, -20.354485, -0.49906468, -0.6680341, -21.275528, -0.49906445, -0.580003, -21.275528, -0.82769513, -0.6780064, -20.354485, -0.8842943, -0.78126955, -20.354485, -0.49906468, -0.71067405, -22.029617, -0.9031601, -0.580003, -21.275528, -0.82769513, -0.6680341, -21.275528, -0.49906445, -0.71067405, -22.029617, -0.9031601, -0.6680341, -21.275528, -0.49906445, -0.8189645, -22.029617, -0.49906397, -0.6680341, -21.275528, -0.49906445, -0.6780064, -20.354485, -0.11391497, -0.580003, -21.275528, -0.17051363, -0.6680341, -21.275528, -0.49906445, -0.78126955, -20.354485, -0.49906468, -0.6780064, -20.354485, -0.11391497, -0.8189645, -22.029617, -0.49906397, -0.6680341, -21.275528, -0.49906445, -0.580003, -21.275528, -0.17051363, -0.8189645, -22.029617, -0.49906397, -0.580003, -21.275528, -0.17051363, -0.71067405, -22.029617, -0.09504819, -0.580003, -21.275528, -0.17051363, -0.3960147, -20.354485, 0.16813636, -0.339396, -21.275528, 0.07004857, -0.580003, -21.275528, -0.17051363, -0.6780064, -20.354485, -0.11391497, -0.3960147, -20.354485, 0.16813636, -0.71067405, -22.029617, -0.09504819, -0.580003, -21.275528, -0.17051363, -0.339396, -21.275528, 0.07004857, -0.71067405, -22.029617, -0.09504819, -0.339396, -21.275528, 0.07004857, -0.4148612, -22.029617, 0.2007823, -0.339396, -21.275528, 0.07004857, -0.010835171, -20.354485, 0.27131462, -0.010835171, -21.275528, 0.15811706, -0.339396, -21.275528, 0.07004857, -0.3960147, -20.354485, 0.16813636, -0.010835171, -20.354485, 0.27131462, -0.4148612, -22.029617, 0.2007823, -0.339396, -21.275528, 0.07004857, -0.010835171, -21.275528, 0.15811706, -0.4148612, -22.029617, 0.2007823, -0.010835171, -21.275528, 0.15811706, -0.010835171, -22.029617, 0.3090477, -0.010835171, -21.275528, 0.15811706, 0.37442207, -20.354485, 0.16813636, 0.31780314, -21.275528, 0.07004857, -0.010835171, -21.275528, 0.15811706, -0.010835171, -20.354485, 0.27131462, 0.37442207, -20.354485, 0.16813636, -0.010835171, -22.029617, 0.3090477, -0.010835171, -21.275528, 0.15811706, 0.31780314, -21.275528, 0.07004857, -0.010835171, -22.029617, 0.3090477, 0.31780314, -21.275528, 0.07004857, 0.39326835, -22.029617, 0.2007823, 0.31780314, -21.275528, 0.07004857, 0.65641344, -20.354485, -0.11391497, 0.5583327, -21.275528, -0.17051363, 0.31780314, -21.275528, 0.07004857, 0.37442207, -20.354485, 0.16813636, 0.65641344, -20.354485, -0.11391497, 0.39326835, -22.029617, 0.2007823, 0.31780314, -21.275528, 0.07004857, 0.5583327, -21.275528, -0.17051363, 0.39326835, -22.029617, 0.2007823, 0.5583327, -21.275528, -0.17051363, 0.6890812, -22.029617, -0.09504819, 0.5583327, -21.275528, -0.17051363, 0.759599, -20.354485, -0.49906468, 0.64644134, -21.275528, -0.49906445, 0.5583327, -21.275528, -0.17051363, 0.65641344, -20.354485, -0.11391497, 0.759599, -20.354485, -0.49906468, 0.6890812, -22.029617, -0.09504819, 0.5583327, -21.275528, -0.17051363, 0.64644134, -21.275528, -0.49906445, 0.6890812, -22.029617, -0.09504819, 0.64644134, -21.275528, -0.49906445, 0.7973716, -22.029617, -0.49906397, 0.64644134, -21.275528, -0.49906445, 0.65641344, -20.354485, -0.8842943, 0.5583327, -21.275528, -0.82769513, 0.64644134, -21.275528, -0.49906445, 0.759599, -20.354485, -0.49906468, 0.65641344, -20.354485, -0.8842943, 0.7973716, -22.029617, -0.49906397, 0.64644134, -21.275528, -0.49906445, 0.5583327, -21.275528, -0.82769513, 0.7973716, -22.029617, -0.49906397, 0.5583327, -21.275528, -0.82769513, 0.6890812, -22.029617, -0.9031601, 0.39326835, -22.029617, -1.1989906, -0.010835171, -22.029617, -0.49906397, 0.6890812, -22.029617, -0.9031601, 0.6890812, -22.029617, -0.9031601, -0.010835171, -22.029617, -0.49906397, 0.7973716, -22.029617, -0.49906397, -0.010835171, -22.029617, -1.307256, -0.010835171, -22.029617, -0.49906397, 0.39326835, -22.029617, -1.1989906, 0.7973716, -22.029617, -0.49906397, -0.010835171, -22.029617, -0.49906397, 0.6890812, -22.029617, -0.09504819, -0.4148612, -22.029617, -1.1989906, -0.010835171, -22.029617, -0.49906397, -0.010835171, -22.029617, -1.307256, 0.6890812, -22.029617, -0.09504819, -0.010835171, -22.029617, -0.49906397, 0.39326835, -22.029617, 0.2007823, -0.71067405, -22.029617, -0.9031601, -0.010835171, -22.029617, -0.49906397, -0.4148612, -22.029617, -1.1989906, 0.39326835, -22.029617, 0.2007823, -0.010835171, -22.029617, -0.49906397, -0.010835171, -22.029617, 0.3090477, -0.8189645, -22.029617, -0.49906397, -0.010835171, -22.029617, -0.49906397, -0.71067405, -22.029617, -0.9031601, -0.010835171, -22.029617, 0.3090477, -0.010835171, -22.029617, -0.49906397, -0.4148612, -22.029617, 0.2007823, -0.71067405, -22.029617, -0.09504819, -0.010835171, -22.029617, -0.49906397, -0.8189645, -22.029617, -0.49906397, -0.4148612, -22.029617, 0.2007823, -0.010835171, -22.029617, -0.49906397, -0.71067405, -22.029617, -0.09504819, 9.266797, -17.581049, 9.7409115, 10.985607, -16.33984, 10.792019, 10.863857, -18.049467, 10.526733, 10.863857, -18.049467, 10.526733, 10.985607, -16.33984, 10.792019, 13.068386, -16.296267, 9.768465, 10.863857, -18.049467, 10.526733, 13.068386, -16.296267, 9.768465, 12.696516, -17.604118, 9.563199, 9.266797, -17.581049, 9.7409115, 9.008772, -16.366539, 10.340904, 10.985607, -16.33984, 10.792019, 8.624725, -17.931135, 7.6034517, 9.008772, -16.366539, 10.340904, 9.266797, -17.581049, 9.7409115, 10.985607, -16.33984, 10.792019, 12.4669, -15.041601, 9.18321, 13.068386, -16.296267, 9.768465, 8.624725, -17.931135, 7.6034517, 8.497637, -16.245218, 7.7565994, 9.008772, -16.366539, 10.340904, 9.600649, -17.456738, 5.615509, 8.497637, -16.245218, 7.7565994, 8.624725, -17.931135, 7.6034517, 9.008772, -16.366539, 10.340904, 11.213087, -14.61334, 9.582636, 10.985607, -16.33984, 10.792019, 10.985607, -16.33984, 10.792019, 11.213087, -14.61334, 9.582636, 12.4669, -15.041601, 9.18321, 8.497637, -16.245218, 7.7565994, 9.8885765, -15.072786, 9.437603, 9.008772, -16.366539, 10.340904, 9.008772, -16.366539, 10.340904, 9.8885765, -15.072786, 9.437603, 11.213087, -14.61334, 9.582636, 9.600649, -17.456738, 5.615509, 9.33643, -16.15444, 5.2154427, 8.497637, -16.245218, 7.7565994, 9.33643, -16.15444, 5.2154427, 9.600649, -17.456738, 5.615509, 11.392937, -17.965525, 4.8956895, 8.497637, -16.245218, 7.7565994, 9.720263, -14.589846, 7.952894, 9.8885765, -15.072786, 9.437603, 9.33643, -16.15444, 5.2154427, 9.720263, -14.589846, 7.952894, 8.497637, -16.245218, 7.7565994, 9.8885765, -15.072786, 9.437603, 11.288914, -14.037912, 8.018254, 11.213087, -14.61334, 9.582636, 9.8885765, -15.072786, 9.437603, 9.720263, -14.589846, 7.952894, 11.288914, -14.037912, 8.018254, 9.33643, -16.15444, 5.2154427, 10.034036, -14.978592, 6.3855233, 9.720263, -14.589846, 7.952894, 9.33643, -16.15444, 5.2154427, 11.392937, -17.965525, 4.8956895, 11.408957, -16.27256, 4.8937664, 10.034036, -14.978592, 6.3855233, 9.33643, -16.15444, 5.2154427, 11.408957, -16.27256, 4.8937664, 11.408957, -16.27256, 4.8937664, 11.392937, -17.965525, 4.8956895, 13.021395, -17.62377, 6.061926, 9.720263, -14.589846, 7.952894, 10.034036, -14.978592, 6.3855233, 11.318819, -14.596681, 6.3662996, 10.034036, -14.978592, 6.3855233, 11.408957, -16.27256, 4.8937664, 11.318819, -14.596681, 6.3662996, 9.720263, -14.589846, 7.952894, 11.318819, -14.596681, 6.3662996, 11.288914, -14.037912, 8.018254, 11.408957, -16.27256, 4.8937664, 13.021395, -17.62377, 6.061926, 13.375538, -16.407764, 6.04676, 11.318819, -14.596681, 6.3662996, 11.408957, -16.27256, 4.8937664, 13.375538, -16.407764, 6.04676, 13.632067, -18.083643, 7.819184, 13.375538, -16.407764, 6.04676, 13.021395, -17.62377, 6.061926, 11.288914, -14.037912, 8.018254, 11.318819, -14.596681, 6.3662996, 12.603388, -15.091156, 6.7550454, 11.318819, -14.596681, 6.3662996, 13.375538, -16.407764, 6.04676, 12.603388, -15.091156, 6.7550454, 13.632067, -18.083643, 7.819184, 13.896927, -16.367395, 7.9291854, 13.375538, -16.407764, 6.04676, 13.896927, -16.367395, 7.9291854, 12.603388, -15.091156, 6.7550454, 13.375538, -16.407764, 6.04676, 12.696516, -17.604118, 9.563199, 13.896927, -16.367395, 7.9291854, 13.632067, -18.083643, 7.819184, 12.696516, -17.604118, 9.563199, 13.068386, -16.296267, 9.768465, 13.896927, -16.367395, 7.9291854, 13.896927, -16.367395, 7.9291854, 12.811857, -14.620176, 7.996041, 12.603388, -15.091156, 6.7550454, 11.288914, -14.037912, 8.018254, 12.603388, -15.091156, 6.7550454, 12.811857, -14.620176, 7.996041, 13.068386, -16.296267, 9.768465, 12.811857, -14.620176, 7.996041, 13.896927, -16.367395, 7.9291854, 11.213087, -14.61334, 9.582636, 11.288914, -14.037912, 8.018254, 12.811857, -14.620176, 7.996041, 13.068386, -16.296267, 9.768465, 12.4669, -15.041601, 9.18321, 12.811857, -14.620176, 7.996041, 11.213087, -14.61334, 9.582636, 12.811857, -14.620176, 7.996041, 12.4669, -15.041601, 9.18321, 9.600649, -17.456738, 5.615509, 11.105434, -18.574488, 7.6675305, 11.392937, -17.965525, 4.8956895, 9.600649, -17.456738, 5.615509, 8.624725, -17.931135, 7.6034517, 11.105434, -18.574488, 7.6675305, 11.392937, -17.965525, 4.8956895, 11.105434, -18.574488, 7.6675305, 13.632067, -18.083643, 7.819184, 11.392937, -17.965525, 4.8956895, 13.632067, -18.083643, 7.819184, 13.021395, -17.62377, 6.061926, 11.105434, -18.574488, 7.6675305, 12.696516, -17.604118, 9.563199, 13.632067, -18.083643, 7.819184, 11.105434, -18.574488, 7.6675305, 10.863857, -18.049467, 10.526733, 12.696516, -17.604118, 9.563199, 8.624725, -17.931135, 7.6034517, 10.863857, -18.049467, 10.526733, 11.105434, -18.574488, 7.6675305, 8.624725, -17.931135, 7.6034517, 9.266797, -17.581049, 9.7409115, 10.863857, -18.049467, 10.526733, 7.21563, -18.140032, 8.584928, 8.205864, -17.407396, 9.311369, 8.061045, -18.408522, 8.982858, 8.061045, -18.408522, 8.982858, 8.205864, -17.407396, 9.311369, 9.478685, -17.401842, 8.917924, 8.061045, -18.408522, 8.982858, 9.478685, -17.401842, 8.917924, 9.09955, -18.140032, 8.584928, 7.21563, -18.140032, 8.584928, 7.005238, -17.401842, 8.917924, 8.205864, -17.407396, 9.311369, 7.005238, -18.380968, 7.5489845, 7.005238, -17.401842, 8.917924, 7.21563, -18.140032, 8.584928, 8.205864, -17.407396, 9.311369, 9.169825, -16.65511, 8.638754, 9.478685, -17.401842, 8.917924, 7.005238, -18.380968, 7.5489845, 6.93411, -17.385181, 7.7371626, 7.005238, -17.401842, 8.917924, 7.595832, -18.105644, 6.610869, 6.93411, -17.385181, 7.7371626, 7.005238, -18.380968, 7.5489845, 7.005238, -17.401842, 8.917924, 8.423091, -16.394949, 8.852991, 8.205864, -17.407396, 9.311369, 8.205864, -17.407396, 9.311369, 8.423091, -16.394949, 8.852991, 9.169825, -16.65511, 8.638754, 6.93411, -17.385181, 7.7371626, 7.595832, -16.65511, 8.638754, 7.005238, -17.401842, 8.917924, 7.005238, -17.401842, 8.917924, 7.595832, -16.65511, 8.638754, 8.423091, -16.394949, 8.852991, 7.595832, -18.105644, 6.610869, 7.571055, -17.368736, 6.5566154, 6.93411, -17.385181, 7.7371626, 7.571055, -17.368736, 6.5566154, 7.595832, -18.105644, 6.610869, 8.423091, -18.353416, 6.1153255, 6.93411, -17.385181, 7.7371626, 7.571055, -16.389395, 7.925341, 7.595832, -16.65511, 8.638754, 7.571055, -17.368736, 6.5566154, 7.571055, -16.389395, 7.925341, 6.93411, -17.385181, 7.7371626, 7.595832, -16.65511, 8.638754, 8.495287, -16.05768, 7.988138, 8.423091, -16.394949, 8.852991, 7.595832, -16.65511, 8.638754, 7.571055, -16.389395, 7.925341, 8.495287, -16.05768, 7.988138, 7.571055, -17.368736, 6.5566154, 7.847235, -16.640371, 7.1145287, 7.571055, -16.389395, 7.925341, 7.571055, -17.368736, 6.5566154, 8.423091, -18.353416, 6.1153255, 8.495287, -17.363182, 6.1631703, 7.847235, -16.640371, 7.1145287, 7.571055, -17.368736, 6.5566154, 8.495287, -17.363182, 6.1631703, 8.495287, -17.363182, 6.1631703, 8.423091, -18.353416, 6.1153255, 9.169825, -18.105644, 6.610869, 7.571055, -16.389395, 7.925341, 7.847235, -16.640371, 7.1145287, 8.495287, -16.384056, 6.9979053, 7.847235, -16.640371, 7.1145287, 8.495287, -17.363182, 6.1631703, 8.495287, -16.384056, 6.9979053, 7.571055, -16.389395, 7.925341, 8.495287, -16.384056, 6.9979053, 8.495287, -16.05768, 7.988138, 8.495287, -17.363182, 6.1631703, 9.169825, -18.105644, 6.610869, 9.347322, -17.368736, 6.5566154, 8.495287, -16.384056, 6.9979053, 8.495287, -17.363182, 6.1631703, 9.347322, -17.368736, 6.5566154, 9.478685, -18.380968, 7.5489845, 9.347322, -17.368736, 6.5566154, 9.169825, -18.105644, 6.610869, 8.495287, -16.05768, 7.988138, 8.495287, -16.384056, 6.9979053, 9.1112995, -16.640371, 7.1145287, 8.495287, -16.384056, 6.9979053, 9.347322, -17.368736, 6.5566154, 9.1112995, -16.640371, 7.1145287, 9.478685, -18.380968, 7.5489845, 9.767254, -17.385181, 7.7371626, 9.347322, -17.368736, 6.5566154, 9.767254, -17.385181, 7.7371626, 9.1112995, -16.640371, 7.1145287, 9.347322, -17.368736, 6.5566154, 9.09955, -18.140032, 8.584928, 9.767254, -17.385181, 7.7371626, 9.478685, -18.380968, 7.5489845, 9.09955, -18.140032, 8.584928, 9.478685, -17.401842, 8.917924, 9.767254, -17.385181, 7.7371626, 9.767254, -17.385181, 7.7371626, 9.347322, -16.389395, 7.925341, 9.1112995, -16.640371, 7.1145287, 8.495287, -16.05768, 7.988138, 9.1112995, -16.640371, 7.1145287, 9.347322, -16.389395, 7.925341, 9.478685, -17.401842, 8.917924, 9.347322, -16.389395, 7.925341, 9.767254, -17.385181, 7.7371626, 8.423091, -16.394949, 8.852991, 8.495287, -16.05768, 7.988138, 9.347322, -16.389395, 7.925341, 9.478685, -17.401842, 8.917924, 9.169825, -16.65511, 8.638754, 9.347322, -16.389395, 7.925341, 8.423091, -16.394949, 8.852991, 9.347322, -16.389395, 7.925341, 9.169825, -16.65511, 8.638754, 7.595832, -18.105644, 6.610869, 8.205864, -18.712898, 7.486401, 8.423091, -18.353416, 6.1153255, 7.595832, -18.105644, 6.610869, 7.005238, -18.380968, 7.5489845, 8.205864, -18.712898, 7.486401, 8.423091, -18.353416, 6.1153255, 8.205864, -18.712898, 7.486401, 9.478685, -18.380968, 7.5489845, 8.423091, -18.353416, 6.1153255, 9.478685, -18.380968, 7.5489845, 9.169825, -18.105644, 6.610869, 8.205864, -18.712898, 7.486401, 9.09955, -18.140032, 8.584928, 9.478685, -18.380968, 7.5489845, 8.205864, -18.712898, 7.486401, 8.061045, -18.408522, 8.982858, 9.09955, -18.140032, 8.584928, 7.005238, -18.380968, 7.5489845, 8.061045, -18.408522, 8.982858, 8.205864, -18.712898, 7.486401, 7.005238, -18.380968, 7.5489845, 7.21563, -18.140032, 8.584928, 8.061045, -18.408522, 8.982858, 11.220137, -19.80587, 7.9881396, 9.957781, -19.472874, 7.7623677, 11.16268, -19.969059, 7.7499795, 11.220137, -19.80587, 7.9881396, 10.0568905, -19.342154, 7.988139, 9.957781, -19.472874, 7.7623677, 10.0568905, -19.342154, 7.988139, 8.76399, -18.20817, 7.922565, 9.957781, -19.472874, 7.7623677, 10.0568905, -19.342154, 7.988139, 8.811623, -18.20881, 7.9881387, 8.76399, -18.20817, 7.922565, 9.957781, -19.472874, 7.7623677, 8.76399, -18.20817, 7.922565, 8.686882, -18.207743, 7.9475555, 11.16268, -19.969059, 7.7499795, 9.957781, -19.472874, 7.7623677, 9.797157, -19.68412, 7.848661, 9.957781, -19.472874, 7.7623677, 8.686882, -18.207743, 7.9475555, 9.797157, -19.68412, 7.848661, 11.16268, -19.969059, 7.7499795, 9.797157, -19.68412, 7.848661, 11.069338, -20.23285, 7.8409715, 9.797157, -19.68412, 7.848661, 8.686882, -18.207743, 7.9475555, 8.686882, -18.207743, 8.028723, 11.069338, -20.23285, 7.8409715, 9.797157, -19.68412, 7.848661, 9.797157, -19.68412, 8.127618, 9.797157, -19.68412, 7.848661, 8.686882, -18.207743, 8.028723, 9.797157, -19.68412, 8.127618, 11.069338, -20.23285, 7.8409715, 9.797157, -19.68412, 8.127618, 11.069338, -20.23285, 8.135094, 9.797157, -19.68412, 8.127618, 8.686882, -18.207743, 8.028723, 8.76399, -18.20817, 8.053713, 11.069338, -20.23285, 8.135094, 9.797157, -19.68412, 8.127618, 9.957781, -19.472874, 8.213911, 9.797157, -19.68412, 8.127618, 8.76399, -18.20817, 8.053713, 9.957781, -19.472874, 8.213911, 9.957781, -19.472874, 8.213911, 8.76399, -18.20817, 8.053713, 8.811623, -18.20881, 7.9881387, 11.069338, -20.23285, 8.135094, 9.957781, -19.472874, 8.213911, 11.16268, -19.969059, 8.226086, 9.957781, -19.472874, 8.213911, 8.811623, -18.20881, 7.9881387, 10.0568905, -19.342154, 7.988139, 11.16268, -19.969059, 8.226086, 9.957781, -19.472874, 8.213911, 10.0568905, -19.342154, 7.988139, 11.16268, -19.969059, 8.226086, 10.0568905, -19.342154, 7.988139, 11.220137, -19.80587, 7.9881396, 10.050482, -22.030905, 7.98814, 11.049473, -19.97397, 8.213911, 10.477888, -22.030905, 8.612482, 10.050482, -22.030905, 7.98814, 10.888208, -19.944494, 7.9881396, 11.049473, -19.97397, 8.213911, 10.888208, -19.944494, 7.9881396, 11.13064, -17.907639, 8.087889, 11.049473, -19.97397, 8.213911, 10.888208, -19.944494, 7.9881396, 11.058659, -17.900164, 7.9881387, 11.13064, -17.907639, 8.087889, 11.049473, -19.97397, 8.213911, 11.13064, -17.907639, 8.087889, 11.247263, -17.92024, 8.049868, 10.477888, -22.030905, 8.612482, 11.049473, -19.97397, 8.213911, 11.310488, -20.021389, 8.127618, 11.049473, -19.97397, 8.213911, 11.247263, -17.92024, 8.049868, 11.310488, -20.021389, 8.127618, 10.477888, -22.030905, 8.612482, 11.310488, -20.021389, 8.127618, 11.169514, -22.031118, 8.374109, 11.310488, -20.021389, 8.127618, 11.247263, -17.92024, 8.049868, 11.247263, -17.92024, 7.9264092, 11.169514, -22.031118, 8.374109, 11.310488, -20.021389, 8.127618, 11.310488, -20.021389, 7.848661, 11.310488, -20.021389, 8.127618, 11.247263, -17.92024, 7.9264092, 11.310488, -20.021389, 7.848661, 11.169514, -22.031118, 8.374109, 11.310488, -20.021389, 7.848661, 11.169514, -22.031118, 7.6021714, 11.310488, -20.021389, 7.848661, 11.247263, -17.92024, 7.9264092, 11.13064, -17.907639, 7.888389, 11.169514, -22.031118, 7.6021714, 11.310488, -20.021389, 7.848661, 11.049473, -19.97397, 7.762368, 11.310488, -20.021389, 7.848661, 11.13064, -17.907639, 7.888389, 11.049473, -19.97397, 7.762368, 11.049473, -19.97397, 7.762368, 11.13064, -17.907639, 7.888389, 11.058659, -17.900164, 7.9881387, 11.169514, -22.031118, 7.6021714, 11.049473, -19.97397, 7.762368, 10.477888, -22.030905, 7.3637977, 11.049473, -19.97397, 7.762368, 11.058659, -17.900164, 7.9881387, 10.888208, -19.944494, 7.9881396, 10.477888, -22.030905, 7.3637977, 11.049473, -19.97397, 7.762368, 10.888208, -19.944494, 7.9881396, 10.477888, -22.030905, 7.3637977, 10.888208, -19.944494, 7.9881396, 10.050482, -22.030905, 7.98814, 11.023628, -19.860125, 7.836486, 10.902519, -19.637772, 6.988935, 11.16268, -19.969059, 7.7499795, 11.023628, -19.860125, 7.836486, 10.7801285, -19.55041, 7.099151, 10.902519, -19.637772, 6.988935, 10.7801285, -19.55041, 7.099151, 10.55094, -18.793425, 6.265697, 10.902519, -19.637772, 6.988935, 10.7801285, -19.55041, 7.099151, 10.519327, -18.793852, 6.309698, 10.55094, -18.793425, 6.265697, 10.902519, -19.637772, 6.988935, 10.55094, -18.793425, 6.265697, 10.5189, -18.792997, 6.2221236, 11.16268, -19.969059, 7.7499795, 10.902519, -19.637772, 6.988935, 10.814302, -19.778744, 6.9052052, 10.902519, -19.637772, 6.988935, 10.5189, -18.792997, 6.2221236, 10.814302, -19.778744, 6.9052052, 11.16268, -19.969059, 7.7499795, 10.814302, -19.778744, 6.9052052, 11.085358, -20.145061, 7.710037, 10.814302, -19.778744, 6.9052052, 10.5189, -18.792997, 6.2221236, 10.4674225, -18.792997, 6.238998, 11.085358, -20.145061, 7.710037, 10.814302, -19.778744, 6.9052052, 10.637232, -19.778744, 6.963517, 10.814302, -19.778744, 6.9052052, 10.4674225, -18.792997, 6.238998, 10.637232, -19.778744, 6.963517, 11.085358, -20.145061, 7.710037, 10.637232, -19.778744, 6.963517, 10.898888, -20.145061, 7.771766, 10.637232, -19.778744, 6.963517, 10.4674225, -18.792997, 6.238998, 10.46785, -18.793425, 6.293038, 10.898888, -20.145061, 7.771766, 10.637232, -19.778744, 6.963517, 10.616299, -19.637772, 7.083345, 10.637232, -19.778744, 6.963517, 10.46785, -18.793425, 6.293038, 10.616299, -19.637772, 7.083345, 10.616299, -19.637772, 7.083345, 10.46785, -18.793425, 6.293038, 10.519327, -18.793852, 6.309698, 10.898888, -20.145061, 7.771766, 10.616299, -19.637772, 7.083345, 10.860653, -19.969059, 7.849729, 10.616299, -19.637772, 7.083345, 10.519327, -18.793852, 6.309698, 10.7801285, -19.55041, 7.099151, 10.860653, -19.969059, 7.849729, 10.616299, -19.637772, 7.083345, 10.7801285, -19.55041, 7.099151, 10.860653, -19.969059, 7.849729, 10.7801285, -19.55041, 7.099151, 11.023628, -19.860125, 7.836486, 9.806982, -18.747929, 5.422632, 9.553658, -18.258793, 6.2026863, 9.731583, -18.927135, 6.042062, 9.731583, -18.927135, 6.042062, 9.553658, -18.258793, 6.2026863, 10.069706, -18.254948, 6.927205, 9.731583, -18.927135, 6.042062, 10.069706, -18.254948, 6.927205, 10.201282, -18.747929, 6.6170635, 9.806982, -18.747929, 5.422632, 9.551735, -18.254948, 5.359194, 9.553658, -18.258793, 6.2026863, 10.419577, -18.908766, 5.0725474, 9.551735, -18.254948, 5.359194, 9.806982, -18.747929, 5.422632, 9.553658, -18.258793, 6.2026863, 10.181845, -17.756413, 6.672812, 10.069706, -18.254948, 6.927205, 10.419577, -18.908766, 5.0725474, 10.285225, -18.244055, 5.06678, 9.551735, -18.254948, 5.359194, 11.137901, -18.725073, 5.250473, 10.285225, -18.244055, 5.06678, 10.419577, -18.908766, 5.0725474, 9.551735, -18.254948, 5.359194, 9.889858, -17.582973, 6.244337, 9.553658, -18.258793, 6.2026863, 9.553658, -18.258793, 6.2026863, 9.889858, -17.582973, 6.244337, 10.181845, -17.756413, 6.672812, 10.285225, -18.244055, 5.06678, 9.852264, -17.756414, 5.6751027, 9.551735, -18.254948, 5.359194, 9.551735, -18.254948, 5.359194, 9.852264, -17.756414, 5.6751027, 9.889858, -17.582973, 6.244337, 11.137901, -18.725073, 5.250473, 11.16695, -18.232948, 5.223346, 10.285225, -18.244055, 5.06678, 11.16695, -18.232948, 5.223346, 11.137901, -18.725073, 5.250473, 11.625116, -18.890398, 5.671045, 10.285225, -18.244055, 5.06678, 10.299322, -17.579128, 5.5099926, 9.852264, -17.756414, 5.6751027, 11.16695, -18.232948, 5.223346, 10.299322, -17.579128, 5.5099926, 10.285225, -18.244055, 5.06678, 9.852264, -17.756414, 5.6751027, 10.453112, -17.357628, 6.1091304, 9.889858, -17.582973, 6.244337, 9.852264, -17.756414, 5.6751027, 10.299322, -17.579128, 5.5099926, 10.453112, -17.357628, 6.1091304, 11.16695, -18.232948, 5.223346, 10.871334, -17.746588, 5.5153327, 10.299322, -17.579128, 5.5099926, 11.16695, -18.232948, 5.223346, 11.625116, -18.890398, 5.671045, 11.610165, -18.229317, 5.727007, 10.871334, -17.746588, 5.5153327, 11.16695, -18.232948, 5.223346, 11.610165, -18.229317, 5.727007, 11.610165, -18.229317, 5.727007, 11.625116, -18.890398, 5.671045, 11.467482, -18.725073, 6.2481823, 10.299322, -17.579128, 5.5099926, 10.871334, -17.746588, 5.5153327, 11.080872, -17.575497, 5.9017286, 10.871334, -17.746588, 5.5153327, 11.610165, -18.229317, 5.727007, 11.080872, -17.575497, 5.9017286, 10.299322, -17.579128, 5.5099926, 11.080872, -17.575497, 5.9017286, 10.453112, -17.357628, 6.1091304, 11.610165, -18.229317, 5.727007, 11.467482, -18.725073, 6.2481823, 11.539036, -18.232946, 6.3494267, 11.080872, -17.575497, 5.9017286, 11.610165, -18.229317, 5.727007, 11.539036, -18.232946, 6.3494267, 10.937548, -18.908766, 6.640559, 11.539036, -18.232946, 6.3494267, 11.467482, -18.725073, 6.2481823, 10.453112, -17.357628, 6.1091304, 11.080872, -17.575497, 5.9017286, 11.135979, -17.746588, 6.316746, 11.080872, -17.575497, 5.9017286, 11.539036, -18.232946, 6.3494267, 11.135979, -17.746588, 6.316746, 10.937548, -18.908766, 6.640559, 10.878382, -18.244053, 6.8629127, 11.539036, -18.232946, 6.3494267, 10.878382, -18.244053, 6.8629127, 11.135979, -17.746588, 6.316746, 11.539036, -18.232946, 6.3494267, 10.201282, -18.747929, 6.6170635, 10.878382, -18.244053, 6.8629127, 10.937548, -18.908766, 6.640559, 10.201282, -18.747929, 6.6170635, 10.069706, -18.254948, 6.927205, 10.878382, -18.244053, 6.8629127, 10.878382, -18.244053, 6.8629127, 10.671408, -17.579128, 6.636073, 11.135979, -17.746588, 6.316746, 10.453112, -17.357628, 6.1091304, 11.135979, -17.746588, 6.316746, 10.671408, -17.579128, 6.636073, 10.069706, -18.254948, 6.927205, 10.671408, -17.579128, 6.636073, 10.878382, -18.244053, 6.8629127, 9.889858, -17.582973, 6.244337, 10.453112, -17.357628, 6.1091304, 10.671408, -17.579128, 6.636073, 10.069706, -18.254948, 6.927205, 10.181845, -17.756413, 6.672812, 10.671408, -17.579128, 6.636073, 9.889858, -17.582973, 6.244337, 10.671408, -17.579128, 6.636073, 10.181845, -17.756413, 6.672812, 11.137901, -18.725073, 5.250473, 10.710709, -19.13048, 5.8203487, 11.625116, -18.890398, 5.671045, 11.137901, -18.725073, 5.250473, 10.419577, -18.908766, 5.0725474, 10.710709, -19.13048, 5.8203487, 11.625116, -18.890398, 5.671045, 10.710709, -19.13048, 5.8203487, 10.937548, -18.908766, 6.640559, 11.625116, -18.890398, 5.671045, 10.937548, -18.908766, 6.640559, 11.467482, -18.725073, 6.2481823, 10.710709, -19.13048, 5.8203487, 10.201282, -18.747929, 6.6170635, 10.937548, -18.908766, 6.640559, 10.710709, -19.13048, 5.8203487, 9.731583, -18.927135, 6.042062, 10.201282, -18.747929, 6.6170635, 10.419577, -18.908766, 5.0725474, 9.731583, -18.927135, 6.042062, 10.710709, -19.13048, 5.8203487, 10.419577, -18.908766, 5.0725474, 9.806982, -18.747929, 5.422632, 9.731583, -18.927135, 6.042062, 15.540274, 10.966449, -3.3071837, 18.560066, 13.1471405, -1.460484, 18.346163, 10.143484, -1.9265671, 18.346163, 10.143484, -1.9265671, 18.560066, 13.1471405, -1.460484, 22.219316, 13.223694, -3.2587752, 18.346163, 10.143484, -1.9265671, 22.219316, 13.223694, -3.2587752, 21.565973, 10.92592, -3.6194077, 15.540274, 10.966449, -3.3071837, 15.086948, 13.100231, -2.2530527, 18.560066, 13.1471405, -1.460484, 14.412214, 10.35138, -7.0625033, 15.086948, 13.100231, -2.2530527, 15.540274, 10.966449, -3.3071837, 18.560066, 13.1471405, -1.460484, 21.16256, 15.42765, -4.2870145, 22.219316, 13.223694, -3.2587752, 14.412214, 10.35138, -7.0625033, 14.188929, 13.313381, -6.7934375, 15.086948, 13.100231, -2.2530527, 16.12682, 11.184852, -10.555136, 14.188929, 13.313381, -6.7934375, 14.412214, 10.35138, -7.0625033, 15.086948, 13.100231, -2.2530527, 18.959728, 16.180443, -3.5852609, 18.560066, 13.1471405, -1.460484, 18.560066, 13.1471405, -1.460484, 18.959728, 16.180443, -3.5852609, 21.16256, 15.42765, -4.2870145, 14.188929, 13.313381, -6.7934375, 16.632683, 15.373238, -3.8400683, 15.086948, 13.100231, -2.2530527, 15.086948, 13.100231, -2.2530527, 16.632683, 15.373238, -3.8400683, 18.959728, 16.180443, -3.5852609, 16.12682, 11.184852, -10.555136, 15.662611, 13.473244, -11.258017, 14.188929, 13.313381, -6.7934375, 15.662611, 13.473244, -11.258017, 16.12682, 11.184852, -10.555136, 19.275705, 10.291335, -11.819794, 14.188929, 13.313381, -6.7934375, 16.33697, 16.22172, -6.448566, 16.632683, 15.373238, -3.8400683, 15.662611, 13.473244, -11.258017, 16.33697, 16.22172, -6.448566, 14.188929, 13.313381, -6.7934375, 16.632683, 15.373238, -3.8400683, 19.092949, 17.191416, -6.333734, 18.959728, 16.180443, -3.5852609, 16.632683, 15.373238, -3.8400683, 16.33697, 16.22172, -6.448566, 19.092949, 17.191416, -6.333734, 15.662611, 13.473244, -11.258017, 16.88824, 15.538728, -9.202293, 16.33697, 16.22172, -6.448566, 15.662611, 13.473244, -11.258017, 19.275705, 10.291335, -11.819794, 19.30385, 13.265345, -11.823172, 16.88824, 15.538728, -9.202293, 15.662611, 13.473244, -11.258017, 19.30385, 13.265345, -11.823172, 19.30385, 13.265345, -11.823172, 19.275705, 10.291335, -11.819794, 22.136759, 10.891392, -9.770823, 16.33697, 16.22172, -6.448566, 16.88824, 15.538728, -9.202293, 19.145487, 16.20971, -9.236067, 16.88824, 15.538728, -9.202293, 19.30385, 13.265345, -11.823172, 19.145487, 16.20971, -9.236067, 16.33697, 16.22172, -6.448566, 19.145487, 16.20971, -9.236067, 19.092949, 17.191416, -6.333734, 19.30385, 13.265345, -11.823172, 22.136759, 10.891392, -9.770823, 22.758955, 13.0278, -9.797468, 19.145487, 16.20971, -9.236067, 19.30385, 13.265345, -11.823172, 22.758955, 13.0278, -9.797468, 23.209654, 10.083063, -6.6834817, 22.758955, 13.0278, -9.797468, 22.136759, 10.891392, -9.770823, 19.092949, 17.191416, -6.333734, 19.145487, 16.20971, -9.236067, 21.402355, 15.340961, -8.553076, 19.145487, 16.20971, -9.236067, 22.758955, 13.0278, -9.797468, 21.402355, 15.340961, -8.553076, 23.209654, 10.083063, -6.6834817, 23.674988, 13.098728, -6.4902196, 22.758955, 13.0278, -9.797468, 23.674988, 13.098728, -6.4902196, 21.402355, 15.340961, -8.553076, 22.758955, 13.0278, -9.797468, 21.565973, 10.92592, -3.6194077, 23.674988, 13.098728, -6.4902196, 23.209654, 10.083063, -6.6834817, 21.565973, 10.92592, -3.6194077, 22.219316, 13.223694, -3.2587752, 23.674988, 13.098728, -6.4902196, 23.674988, 13.098728, -6.4902196, 21.768618, 16.168056, -6.372762, 21.402355, 15.340961, -8.553076, 19.092949, 17.191416, -6.333734, 21.402355, 15.340961, -8.553076, 21.768618, 16.168056, -6.372762, 22.219316, 13.223694, -3.2587752, 21.768618, 16.168056, -6.372762, 23.674988, 13.098728, -6.4902196, 18.959728, 16.180443, -3.5852609, 19.092949, 17.191416, -6.333734, 21.768618, 16.168056, -6.372762, 22.219316, 13.223694, -3.2587752, 21.16256, 15.42765, -4.2870145, 21.768618, 16.168056, -6.372762, 18.959728, 16.180443, -3.5852609, 21.768618, 16.168056, -6.372762, 21.16256, 15.42765, -4.2870145, 16.12682, 11.184852, -10.555136, 18.770592, 9.221069, -6.949922, 19.275705, 10.291335, -11.819794, 16.12682, 11.184852, -10.555136, 14.412214, 10.35138, -7.0625033, 18.770592, 9.221069, -6.949922, 19.275705, 10.291335, -11.819794, 18.770592, 9.221069, -6.949922, 23.209654, 10.083063, -6.6834817, 19.275705, 10.291335, -11.819794, 23.209654, 10.083063, -6.6834817, 22.136759, 10.891392, -9.770823, 18.770592, 9.221069, -6.949922, 21.565973, 10.92592, -3.6194077, 23.209654, 10.083063, -6.6834817, 18.770592, 9.221069, -6.949922, 18.346163, 10.143484, -1.9265671, 21.565973, 10.92592, -3.6194077, 14.412214, 10.35138, -7.0625033, 18.346163, 10.143484, -1.9265671, 18.770592, 9.221069, -6.949922, 14.412214, 10.35138, -7.0625033, 15.540274, 10.966449, -3.3071837, 18.346163, 10.143484, -1.9265671, 11.936562, 9.984368, -5.338141, 13.67631, 11.271917, -4.06185, 13.421877, 9.512654, -4.639014, 13.421877, 9.512654, -4.639014, 13.67631, 11.271917, -4.06185, 15.91254, 11.2813, -4.7530966, 13.421877, 9.512654, -4.639014, 15.91254, 11.2813, -4.7530966, 15.246437, 9.984368, -5.338141, 11.936562, 9.984368, -5.338141, 11.566921, 11.2813, -4.7530966, 13.67631, 11.271917, -4.06185, 11.566921, 9.561063, -7.158197, 11.566921, 11.2813, -4.7530966, 11.936562, 9.984368, -5.338141, 13.67631, 11.271917, -4.06185, 15.369902, 12.5936165, -5.243574, 15.91254, 11.2813, -4.7530966, 11.566921, 9.561063, -7.158197, 11.441956, 11.310569, -6.8275857, 11.566921, 11.2813, -4.7530966, 12.604542, 10.044409, -8.806381, 11.441956, 11.310569, -6.8275857, 11.566921, 9.561063, -7.158197, 11.566921, 11.2813, -4.7530966, 14.057959, 13.050319, -4.8671794, 13.67631, 11.271917, -4.06185, 13.67631, 11.271917, -4.06185, 14.057959, 13.050319, -4.8671794, 15.369902, 12.5936165, -5.243574, 11.441956, 11.310569, -6.8275857, 12.604542, 12.5936165, -5.243574, 11.566921, 11.2813, -4.7530966, 11.566921, 11.2813, -4.7530966, 12.604542, 12.5936165, -5.243574, 14.057959, 13.050319, -4.8671794, 12.604542, 10.044409, -8.806381, 12.56101, 11.339464, -8.901699, 11.441956, 11.310569, -6.8275857, 12.56101, 11.339464, -8.901699, 12.604542, 10.044409, -8.806381, 14.057959, 9.609097, -9.677006, 11.441956, 11.310569, -6.8275857, 12.56101, 13.0597, -6.4969745, 12.604542, 12.5936165, -5.243574, 12.56101, 11.339464, -8.901699, 12.56101, 13.0597, -6.4969745, 11.441956, 11.310569, -6.8275857, 12.604542, 12.5936165, -5.243574, 14.184801, 13.643244, -6.386646, 14.057959, 13.050319, -4.8671794, 12.604542, 12.5936165, -5.243574, 12.56101, 13.0597, -6.4969745, 14.184801, 13.643244, -6.386646, 12.56101, 11.339464, -8.901699, 13.046232, 12.619133, -7.921497, 12.56101, 13.0597, -6.4969745, 12.56101, 11.339464, -8.901699, 14.057959, 9.609097, -9.677006, 14.184801, 11.34922, -9.592945, 13.046232, 12.619133, -7.921497, 12.56101, 11.339464, -8.901699, 14.184801, 11.34922, -9.592945, 14.184801, 11.34922, -9.592945, 14.057959, 9.609097, -9.677006, 15.369902, 10.044409, -8.806381, 12.56101, 13.0597, -6.4969745, 13.046232, 12.619133, -7.921497, 14.184801, 13.069456, -8.126394, 13.046232, 12.619133, -7.921497, 14.184801, 11.34922, -9.592945, 14.184801, 13.069456, -8.126394, 12.56101, 13.0597, -6.4969745, 14.184801, 13.069456, -8.126394, 14.184801, 13.643244, -6.386646, 14.184801, 11.34922, -9.592945, 15.369902, 10.044409, -8.806381, 15.681749, 11.339464, -8.901699, 14.184801, 13.069456, -8.126394, 14.184801, 11.34922, -9.592945, 15.681749, 11.339464, -8.901699, 15.91254, 9.561063, -7.158197, 15.681749, 11.339464, -8.901699, 15.369902, 10.044409, -8.806381, 14.184801, 13.643244, -6.386646, 14.184801, 13.069456, -8.126394, 15.2670765, 12.619133, -7.921497, 14.184801, 13.069456, -8.126394, 15.681749, 11.339464, -8.901699, 15.2670765, 12.619133, -7.921497, 15.91254, 9.561063, -7.158197, 16.419529, 11.310569, -6.8275857, 15.681749, 11.339464, -8.901699, 16.419529, 11.310569, -6.8275857, 15.2670765, 12.619133, -7.921497, 15.681749, 11.339464, -8.901699, 15.246437, 9.984368, -5.338141, 16.419529, 11.310569, -6.8275857, 15.91254, 9.561063, -7.158197, 15.246437, 9.984368, -5.338141, 15.91254, 11.2813, -4.7530966, 16.419529, 11.310569, -6.8275857, 16.419529, 11.310569, -6.8275857, 15.681749, 13.0597, -6.4969745, 15.2670765, 12.619133, -7.921497, 14.184801, 13.643244, -6.386646, 15.2670765, 12.619133, -7.921497, 15.681749, 13.0597, -6.4969745, 15.91254, 11.2813, -4.7530966, 15.681749, 13.0597, -6.4969745, 16.419529, 11.310569, -6.8275857, 14.057959, 13.050319, -4.8671794, 14.184801, 13.643244, -6.386646, 15.681749, 13.0597, -6.4969745, 15.91254, 11.2813, -4.7530966, 15.369902, 12.5936165, -5.243574, 15.681749, 13.0597, -6.4969745, 14.057959, 13.050319, -4.8671794, 15.681749, 13.0597, -6.4969745, 15.369902, 12.5936165, -5.243574, 12.604542, 10.044409, -8.806381, 13.67631, 8.977895, -7.268151, 14.057959, 9.609097, -9.677006, 12.604542, 10.044409, -8.806381, 11.566921, 9.561063, -7.158197, 13.67631, 8.977895, -7.268151, 14.057959, 9.609097, -9.677006, 13.67631, 8.977895, -7.268151, 15.91254, 9.561063, -7.158197, 14.057959, 9.609097, -9.677006, 15.91254, 9.561063, -7.158197, 15.369902, 10.044409, -8.806381, 13.67631, 8.977895, -7.268151, 15.246437, 9.984368, -5.338141, 15.91254, 9.561063, -7.158197, 13.67631, 8.977895, -7.268151, 13.421877, 9.512654, -4.639014, 15.246437, 9.984368, -5.338141, 11.566921, 9.561063, -7.158197, 13.421877, 9.512654, -4.639014, 13.67631, 8.977895, -7.268151, 11.566921, 9.561063, -7.158197, 11.936562, 9.984368, -5.338141, 13.421877, 9.512654, -4.639014, 16.48933, 8.916348, -10.894003, 16.04426, 9.775716, -9.5235195, 16.35686, 8.601498, -9.805721, 16.35686, 8.601498, -9.805721, 16.04426, 9.775716, -9.5235195, 16.95091, 9.782472, -8.250607, 16.35686, 8.601498, -9.805721, 16.95091, 9.782472, -8.250607, 17.182077, 8.916349, -8.795498, 16.48933, 8.916348, -10.894003, 16.040882, 9.78247, -11.005459, 16.04426, 9.775716, -9.5235195, 17.565601, 8.63377, -11.509069, 16.040882, 9.78247, -11.005459, 16.48933, 8.916348, -10.894003, 16.04426, 9.775716, -9.5235195, 17.147928, 10.65835, -8.697552, 16.95091, 9.782472, -8.250607, 17.565601, 8.63377, -11.509069, 17.329557, 9.801608, -11.519203, 16.040882, 9.78247, -11.005459, 18.827633, 8.956502, -11.19647, 17.329557, 9.801608, -11.519203, 17.565601, 8.63377, -11.509069, 16.040882, 9.78247, -11.005459, 16.634933, 10.963444, -9.450344, 16.04426, 9.775716, -9.5235195, 16.04426, 9.775716, -9.5235195, 16.634933, 10.963444, -9.450344, 17.147928, 10.65835, -8.697552, 17.329557, 9.801608, -11.519203, 16.568888, 10.658349, -10.450436, 16.040882, 9.78247, -11.005459, 16.040882, 9.78247, -11.005459, 16.568888, 10.658349, -10.450436, 16.634933, 10.963444, -9.450344, 18.827633, 8.956502, -11.19647, 18.87867, 9.821122, -11.244131, 17.329557, 9.801608, -11.519203, 18.87867, 9.821122, -11.244131, 18.827633, 8.956502, -11.19647, 19.683622, 8.666044, -10.457565, 17.329557, 9.801608, -11.519203, 17.354326, 10.969822, -10.740519, 16.568888, 10.658349, -10.450436, 18.87867, 9.821122, -11.244131, 17.354326, 10.969822, -10.740519, 17.329557, 9.801608, -11.519203, 16.568888, 10.658349, -10.450436, 17.62452, 11.358977, -9.687889, 16.634933, 10.963444, -9.450344, 16.568888, 10.658349, -10.450436, 17.354326, 10.969822, -10.740519, 17.62452, 11.358977, -9.687889, 18.87867, 9.821122, -11.244131, 18.359297, 10.6756115, -10.731136, 17.354326, 10.969822, -10.740519, 18.87867, 9.821122, -11.244131, 19.683622, 8.666044, -10.457565, 19.657354, 9.827502, -10.359245, 18.359297, 10.6756115, -10.731136, 18.87867, 9.821122, -11.244131, 19.657354, 9.827502, -10.359245, 19.657354, 9.827502, -10.359245, 19.683622, 8.666044, -10.457565, 19.406673, 8.956503, -9.443586, 17.354326, 10.969822, -10.740519, 18.359297, 10.6756115, -10.731136, 18.727436, 10.976202, -10.052275, 18.359297, 10.6756115, -10.731136, 19.657354, 9.827502, -10.359245, 18.727436, 10.976202, -10.052275, 17.354326, 10.969822, -10.740519, 18.727436, 10.976202, -10.052275, 17.62452, 11.358977, -9.687889, 19.657354, 9.827502, -10.359245, 19.406673, 8.956503, -9.443586, 19.532389, 9.821123, -9.265711, 18.727436, 10.976202, -10.052275, 19.657354, 9.827502, -10.359245, 19.532389, 9.821123, -9.265711, 18.47563, 8.633772, -8.754217, 19.532389, 9.821123, -9.265711, 19.406673, 8.956503, -9.443586, 17.62452, 11.358977, -9.687889, 18.727436, 10.976202, -10.052275, 18.824255, 10.6756115, -9.323126, 18.727436, 10.976202, -10.052275, 19.532389, 9.821123, -9.265711, 18.824255, 10.6756115, -9.323126, 18.47563, 8.633772, -8.754217, 18.371681, 9.80161, -8.363562, 19.532389, 9.821123, -9.265711, 18.371681, 9.80161, -8.363562, 18.824255, 10.6756115, -9.323126, 19.532389, 9.821123, -9.265711, 17.182077, 8.916349, -8.795498, 18.371681, 9.80161, -8.363562, 18.47563, 8.633772, -8.754217, 17.182077, 8.916349, -8.795498, 16.95091, 9.782472, -8.250607, 18.371681, 9.80161, -8.363562, 18.371681, 9.80161, -8.363562, 18.008045, 10.969824, -8.762098, 18.824255, 10.6756115, -9.323126, 17.62452, 11.358977, -9.687889, 18.824255, 10.6756115, -9.323126, 18.008045, 10.969824, -8.762098, 16.95091, 9.782472, -8.250607, 18.008045, 10.969824, -8.762098, 18.371681, 9.80161, -8.363562, 16.634933, 10.963444, -9.450344, 17.62452, 11.358977, -9.687889, 18.008045, 10.969824, -8.762098, 16.95091, 9.782472, -8.250607, 17.147928, 10.65835, -8.697552, 18.008045, 10.969824, -8.762098, 16.634933, 10.963444, -9.450344, 18.008045, 10.969824, -8.762098, 17.147928, 10.65835, -8.697552, 18.827633, 8.956502, -11.19647, 18.077095, 8.244242, -10.195251, 19.683622, 8.666044, -10.457565, 18.827633, 8.956502, -11.19647, 17.565601, 8.63377, -11.509069, 18.077095, 8.244242, -10.195251, 19.683622, 8.666044, -10.457565, 18.077095, 8.244242, -10.195251, 18.47563, 8.633772, -8.754217, 19.683622, 8.666044, -10.457565, 18.47563, 8.633772, -8.754217, 19.406673, 8.956503, -9.443586, 18.077095, 8.244242, -10.195251, 17.182077, 8.916349, -8.795498, 18.47563, 8.633772, -8.754217, 18.077095, 8.244242, -10.195251, 16.35686, 8.601498, -9.805721, 17.182077, 8.916349, -8.795498, 17.565601, 8.63377, -11.509069, 16.35686, 8.601498, -9.805721, 18.077095, 8.244242, -10.195251, 17.565601, 8.63377, -11.509069, 16.48933, 8.916348, -10.894003, 16.35686, 8.601498, -9.805721, 18.97211, 7.0576415, -6.386642, 16.75427, 7.642686, -6.783302, 18.871164, 6.7709355, -6.804692, 18.97211, 7.0576415, -6.386642, 16.92877, 7.871976, -6.386642, 16.75427, 7.642686, -6.783302, 16.92877, 7.871976, -6.386642, 14.656889, 9.864281, -6.5018516, 16.75427, 7.642686, -6.783302, 16.92877, 7.871976, -6.386642, 14.740574, 9.86353, -6.386643, 14.656889, 9.864281, -6.5018516, 16.75427, 7.642686, -6.783302, 14.656889, 9.864281, -6.5018516, 14.521418, 9.865407, -6.4579444, 18.871164, 6.7709355, -6.804692, 16.75427, 7.642686, -6.783302, 16.472067, 7.2715445, -6.6316934, 16.75427, 7.642686, -6.783302, 14.521418, 9.865407, -6.4579444, 16.472067, 7.2715445, -6.6316934, 18.871164, 6.7709355, -6.804692, 16.472067, 7.2715445, -6.6316934, 18.70717, 6.307478, -6.645202, 16.472067, 7.2715445, -6.6316934, 14.521418, 9.865407, -6.4579444, 14.521418, 9.865407, -6.3153424, 18.70717, 6.307478, -6.645202, 16.472067, 7.2715445, -6.6316934, 16.472067, 7.271545, -6.1415906, 16.472067, 7.2715445, -6.6316934, 14.521418, 9.865407, -6.3153424, 16.472067, 7.271545, -6.1415906, 18.70717, 6.307478, -6.645202, 16.472067, 7.271545, -6.1415906, 18.70717, 6.307478, -6.128081, 16.472067, 7.271545, -6.1415906, 14.521418, 9.865407, -6.3153424, 14.656889, 9.864282, -6.2714353, 18.70717, 6.307478, -6.128081, 16.472067, 7.271545, -6.1415906, 16.75427, 7.642687, -5.9899826, 16.472067, 7.271545, -6.1415906, 14.656889, 9.864282, -6.2714353, 16.75427, 7.642687, -5.9899826, 16.75427, 7.642687, -5.9899826, 14.656889, 9.864282, -6.2714353, 14.740574, 9.86353, -6.386643, 18.70717, 6.307478, -6.128081, 16.75427, 7.642687, -5.9899826, 18.871164, 6.770936, -5.9685917, 16.75427, 7.642687, -5.9899826, 14.740574, 9.86353, -6.386643, 16.92877, 7.871976, -6.386642, 18.871164, 6.770936, -5.9685917, 16.75427, 7.642687, -5.9899826, 16.92877, 7.871976, -6.386642, 18.871164, 6.770936, -5.9685917, 16.92877, 7.871976, -6.386642, 18.97211, 7.0576415, -6.386642, 16.917137, 3.1484604, -6.38664, 18.672272, 6.762305, -5.989982, 17.66805, 3.1484613, -5.289727, 16.917137, 3.1484604, -6.38664, 18.388943, 6.8137164, -6.386642, 18.672272, 6.762305, -5.989982, 18.388943, 6.8137164, -6.386642, 18.814873, 10.392285, -6.211393, 18.672272, 6.762305, -5.989982, 18.388943, 6.8137164, -6.386642, 18.688408, 10.405795, -6.386644, 18.814873, 10.392285, -6.211393, 18.672272, 6.762305, -5.989982, 18.814873, 10.392285, -6.211393, 19.01977, 10.370894, -6.278191, 17.66805, 3.1484613, -5.289727, 18.672272, 6.762305, -5.989982, 19.13085, 6.6789947, -6.1415906, 18.672272, 6.762305, -5.989982, 19.01977, 10.370894, -6.278191, 19.13085, 6.6789947, -6.1415906, 17.66805, 3.1484613, -5.289727, 19.13085, 6.6789947, -6.1415906, 18.883549, 3.148085, -5.708528, 19.13085, 6.6789947, -6.1415906, 19.01977, 10.370894, -6.278191, 19.01977, 10.370894, -6.4950967, 18.883549, 3.148085, -5.708528, 19.13085, 6.6789947, -6.1415906, 19.13085, 6.678994, -6.6316924, 19.13085, 6.6789947, -6.1415906, 19.01977, 10.370894, -6.4950967, 19.13085, 6.678994, -6.6316924, 18.883549, 3.148085, -5.708528, 19.13085, 6.678994, -6.6316924, 18.883549, 3.1480846, -7.064751, 19.13085, 6.678994, -6.6316924, 19.01977, 10.370894, -6.4950967, 18.814873, 10.392285, -6.561895, 18.883549, 3.1480846, -7.064751, 19.13085, 6.678994, -6.6316924, 18.672272, 6.7623043, -6.783302, 19.13085, 6.678994, -6.6316924, 18.814873, 10.392285, -6.561895, 18.672272, 6.7623043, -6.783302, 18.672272, 6.7623043, -6.783302, 18.814873, 10.392285, -6.561895, 18.688408, 10.405795, -6.386644, 18.883549, 3.1480846, -7.064751, 18.672272, 6.7623043, -6.783302, 17.66805, 3.14846, -7.4835525, 18.672272, 6.7623043, -6.783302, 18.688408, 10.405795, -6.386644, 18.388943, 6.8137164, -6.386642, 17.66805, 3.14846, -7.4835525, 18.672272, 6.7623043, -6.783302, 18.388943, 6.8137164, -6.386642, 17.66805, 3.14846, -7.4835525, 18.388943, 6.8137164, -6.386642, 16.917137, 3.1484604, -6.38664, 18.62724, 6.962323, -6.6530833, 18.414085, 7.3529778, -8.142153, 18.871164, 6.7709355, -6.804692, 18.62724, 6.962323, -6.6530833, 18.199057, 7.5060873, -7.9485135, 18.414085, 7.3529778, -8.142153, 18.199057, 7.5060873, -7.9485135, 17.796392, 8.836041, -9.412815, 18.414085, 7.3529778, -8.142153, 18.199057, 7.5060873, -7.9485135, 17.740852, 8.835667, -9.33551, 17.796392, 8.836041, -9.412815, 18.414085, 7.3529778, -8.142153, 17.796392, 8.836041, -9.412815, 17.740103, 8.836792, -9.48937, 18.871164, 6.7709355, -6.804692, 18.414085, 7.3529778, -8.142153, 18.258724, 7.1053, -8.289257, 18.414085, 7.3529778, -8.142153, 17.740103, 8.836792, -9.48937, 18.258724, 7.1053, -8.289257, 18.871164, 6.7709355, -6.804692, 18.258724, 7.1053, -8.289257, 18.735317, 6.461338, -6.8752427, 18.258724, 7.1053, -8.289257, 17.740103, 8.836792, -9.48937, 17.650038, 8.836792, -9.459724, 18.735317, 6.461338, -6.8752427, 18.258724, 7.1053, -8.289257, 17.948002, 7.1053, -8.18681, 18.258724, 7.1053, -8.289257, 17.650038, 8.836792, -9.459724, 17.948002, 7.1053, -8.18681, 18.735317, 6.461338, -6.8752427, 17.948002, 7.1053, -8.18681, 18.407707, 6.461338, -6.7671647, 17.948002, 7.1053, -8.18681, 17.650038, 8.836792, -9.459724, 17.650414, 8.836041, -9.36478, 18.407707, 6.461338, -6.7671647, 17.948002, 7.1053, -8.18681, 17.911226, 7.3529778, -7.975908, 17.948002, 7.1053, -8.18681, 17.650414, 8.836041, -9.36478, 17.911226, 7.3529778, -7.975908, 17.911226, 7.3529778, -7.975908, 17.650414, 8.836041, -9.36478, 17.740852, 8.835667, -9.33551, 18.407707, 6.461338, -6.7671647, 17.911226, 7.3529778, -7.975908, 18.340908, 6.7709355, -6.6298165, 17.911226, 7.3529778, -7.975908, 17.740852, 8.835667, -9.33551, 18.199057, 7.5060873, -7.9485135, 18.340908, 6.7709355, -6.6298165, 17.911226, 7.3529778, -7.975908, 18.199057, 7.5060873, -7.9485135, 18.340908, 6.7709355, -6.6298165, 18.199057, 7.5060873, -7.9485135, 18.62724, 6.962323, -6.6530833, -21.319002, 11.927798, 5.296257, -17.92721, 14.377124, 7.3704467, -18.167463, 11.003454, 6.8469477, -18.167463, 11.003454, 6.8469477, -17.92721, 14.377124, 7.3704467, -13.817183, 14.463108, 5.3506293, -18.167463, 11.003454, 6.8469477, -13.817183, 14.463108, 5.3506293, -14.551012, 11.882277, 4.945571, -21.319002, 11.927798, 5.296257, -21.828173, 14.324436, 6.480244, -17.92721, 14.377124, 7.3704467, -22.586025, 11.236962, 1.0783278, -21.828173, 14.324436, 6.480244, -21.319002, 11.927798, 5.296257, -17.92721, 14.377124, 7.3704467, -15.004122, 16.938564, 4.195724, -13.817183, 14.463108, 5.3506293, -22.586025, 11.236962, 1.0783278, -22.836817, 14.563845, 1.3805406, -21.828173, 14.324436, 6.480244, -20.660202, 12.173107, -2.8445535, -22.836817, 14.563845, 1.3805406, -22.586025, 11.236962, 1.0783278, -21.828173, 14.324436, 6.480244, -17.478313, 17.78409, 4.983925, -17.92721, 14.377124, 7.3704467, -17.92721, 14.377124, 7.3704467, -17.478313, 17.78409, 4.983925, -15.004122, 16.938564, 4.195724, -22.836817, 14.563845, 1.3805406, -20.092022, 16.877449, 4.697728, -21.828173, 14.324436, 6.480244, -21.828173, 14.324436, 6.480244, -20.092022, 16.877449, 4.697728, -17.478313, 17.78409, 4.983925, -20.660202, 12.173107, -2.8445535, -21.181595, 14.743402, -3.6340203, -22.836817, 14.563845, 1.3805406, -21.181595, 14.743402, -3.6340203, -20.660202, 12.173107, -2.8445535, -17.123413, 11.16952, -4.2650013, -22.836817, 14.563845, 1.3805406, -20.424162, 17.830454, 1.7678959, -20.092022, 16.877449, 4.697728, -21.181595, 14.743402, -3.6340203, -20.424162, 17.830454, 1.7678959, -22.836817, 14.563845, 1.3805406, -20.092022, 16.877449, 4.697728, -17.328682, 18.919605, 1.896874, -17.478313, 17.78409, 4.983925, -20.092022, 16.877449, 4.697728, -20.424162, 17.830454, 1.7678959, -17.328682, 18.919605, 1.896874, -21.181595, 14.743402, -3.6340203, -19.804983, 17.063326, -1.3250556, -20.424162, 17.830454, 1.7678959, -21.181595, 14.743402, -3.6340203, -17.123413, 11.16952, -4.2650013, -17.0918, 14.5098915, -4.268796, -19.804983, 17.063326, -1.3250556, -21.181595, 14.743402, -3.6340203, -17.0918, 14.5098915, -4.268796, -17.0918, 14.5098915, -4.268796, -17.123413, 11.16952, -4.2650013, -13.909912, 11.843496, -1.963623, -20.424162, 17.830454, 1.7678959, -19.804983, 17.063326, -1.3250556, -17.269672, 17.816965, -1.3629904, -19.804983, 17.063326, -1.3250556, -17.0918, 14.5098915, -4.268796, -17.269672, 17.816965, -1.3629904, -20.424162, 17.830454, 1.7678959, -17.269672, 17.816965, -1.3629904, -17.328682, 18.919605, 1.896874, -17.0918, 14.5098915, -4.268796, -13.909912, 11.843496, -1.963623, -13.211069, 14.243084, -1.9935503, -17.269672, 17.816965, -1.3629904, -17.0918, 14.5098915, -4.268796, -13.211069, 14.243084, -1.9935503, -12.704849, 10.935591, 1.5040407, -13.211069, 14.243084, -1.9935503, -13.909912, 11.843496, -1.963623, -17.328682, 18.919605, 1.896874, -17.269672, 17.816965, -1.3629904, -14.734785, 16.841198, -0.5958638, -17.269672, 17.816965, -1.3629904, -13.211069, 14.243084, -1.9935503, -14.734785, 16.841198, -0.5958638, -12.704849, 10.935591, 1.5040407, -12.182192, 14.322748, 1.7211109, -13.211069, 14.243084, -1.9935503, -12.182192, 14.322748, 1.7211109, -14.734785, 16.841198, -0.5958638, -13.211069, 14.243084, -1.9935503, -14.551012, 11.882277, 4.945571, -12.182192, 14.322748, 1.7211109, -12.704849, 10.935591, 1.5040407, -14.551012, 11.882277, 4.945571, -13.817183, 14.463108, 5.3506293, -12.182192, 14.322748, 1.7211109, -12.182192, 14.322748, 1.7211109, -14.323402, 17.77018, 1.8530385, -14.734785, 16.841198, -0.5958638, -17.328682, 18.919605, 1.896874, -14.734785, 16.841198, -0.5958638, -14.323402, 17.77018, 1.8530385, -13.817183, 14.463108, 5.3506293, -14.323402, 17.77018, 1.8530385, -12.182192, 14.322748, 1.7211109, -17.478313, 17.78409, 4.983925, -17.328682, 18.919605, 1.896874, -14.323402, 17.77018, 1.8530385, -13.817183, 14.463108, 5.3506293, -15.004122, 16.938564, 4.195724, -14.323402, 17.77018, 1.8530385, -17.478313, 17.78409, 4.983925, -14.323402, 17.77018, 1.8530385, -15.004122, 16.938564, 4.195724, -20.660202, 12.173107, -2.8445535, -17.690748, 9.96741, 1.2047776, -17.123413, 11.16952, -4.2650013, -20.660202, 12.173107, -2.8445535, -22.586025, 11.236962, 1.0783278, -17.690748, 9.96741, 1.2047776, -17.123413, 11.16952, -4.2650013, -17.690748, 9.96741, 1.2047776, -12.704849, 10.935591, 1.5040407, -17.123413, 11.16952, -4.2650013, -12.704849, 10.935591, 1.5040407, -13.909912, 11.843496, -1.963623, -17.690748, 9.96741, 1.2047776, -14.551012, 11.882277, 4.945571, -12.704849, 10.935591, 1.5040407, -17.690748, 9.96741, 1.2047776, -18.167463, 11.003454, 6.8469477, -14.551012, 11.882277, 4.945571, -22.586025, 11.236962, 1.0783278, -18.167463, 11.003454, 6.8469477, -17.690748, 9.96741, 1.2047776, -22.586025, 11.236962, 1.0783278, -21.319002, 11.927798, 5.296257, -18.167463, 11.003454, 6.8469477, -25.366648, 10.824738, 3.0151107, -23.412582, 12.270897, 4.4486246, -23.698359, 10.294915, 3.8003616, -23.698359, 10.294915, 3.8003616, -23.412582, 12.270897, 4.4486246, -20.900877, 12.281435, 3.6722255, -23.698359, 10.294915, 3.8003616, -20.900877, 12.281435, 3.6722255, -21.649036, 10.824738, 3.0151107, -25.366648, 10.824738, 3.0151107, -25.781822, 12.281435, 3.6722255, -23.412582, 12.270897, 4.4486246, -25.781822, 10.349287, 0.97084606, -25.781822, 12.281435, 3.6722255, -25.366648, 10.824738, 3.0151107, -23.412582, 12.270897, 4.4486246, -21.510363, 13.755413, 3.1213274, -20.900877, 12.281435, 3.6722255, -25.781822, 10.349287, 0.97084606, -25.922182, 12.314311, 1.342185, -25.781822, 12.281435, 3.6722255, -24.61638, 10.892176, -0.880373, -25.922182, 12.314311, 1.342185, -25.781822, 10.349287, 0.97084606, -25.781822, 12.281435, 3.6722255, -22.98392, 14.268375, 3.5440893, -23.412582, 12.270897, 4.4486246, -23.412582, 12.270897, 4.4486246, -22.98392, 14.268375, 3.5440893, -21.510363, 13.755413, 3.1213274, -25.922182, 12.314311, 1.342185, -24.61638, 13.755413, 3.1213274, -25.781822, 12.281435, 3.6722255, -25.781822, 12.281435, 3.6722255, -24.61638, 13.755413, 3.1213274, -22.98392, 14.268375, 3.5440893, -24.61638, 10.892176, -0.880373, -24.665274, 12.3467655, -0.9874339, -25.922182, 12.314311, 1.342185, -24.665274, 12.3467655, -0.9874339, -24.61638, 10.892176, -0.880373, -22.98392, 10.403238, -1.8582478, -25.922182, 12.314311, 1.342185, -24.665274, 14.278913, 1.713524, -24.61638, 13.755413, 3.1213274, -24.665274, 12.3467655, -0.9874339, -24.665274, 14.278913, 1.713524, -25.922182, 12.314311, 1.342185, -24.61638, 13.755413, 3.1213274, -22.841454, 14.934341, 1.8374442, -22.98392, 14.268375, 3.5440893, -24.61638, 13.755413, 3.1213274, -24.665274, 14.278913, 1.713524, -22.841454, 14.934341, 1.8374442, -24.665274, 12.3467655, -0.9874339, -24.120277, 13.784074, 0.11351812, -24.665274, 14.278913, 1.713524, -24.665274, 12.3467655, -0.9874339, -22.98392, 10.403238, -1.8582478, -22.841454, 12.357724, -1.763833, -24.120277, 13.784074, 0.11351812, -24.665274, 12.3467655, -0.9874339, -22.841454, 12.357724, -1.763833, -22.841454, 12.357724, -1.763833, -22.98392, 10.403238, -1.8582478, -21.510363, 10.892176, -0.880373, -24.665274, 14.278913, 1.713524, -24.120277, 13.784074, 0.11351812, -22.841454, 14.289871, -0.116620064, -24.120277, 13.784074, 0.11351812, -22.841454, 12.357724, -1.763833, -22.841454, 14.289871, -0.116620064, -24.665274, 14.278913, 1.713524, -22.841454, 14.289871, -0.116620064, -22.841454, 14.934341, 1.8374442, -22.841454, 12.357724, -1.763833, -21.510363, 10.892176, -0.880373, -21.160097, 12.3467655, -0.9874339, -22.841454, 14.289871, -0.116620064, -22.841454, 12.357724, -1.763833, -21.160097, 12.3467655, -0.9874339, -20.900877, 10.349287, 0.97084606, -21.160097, 12.3467655, -0.9874339, -21.510363, 10.892176, -0.880373, -22.841454, 14.934341, 1.8374442, -22.841454, 14.289871, -0.116620064, -21.625853, 13.784074, 0.11351812, -22.841454, 14.289871, -0.116620064, -21.160097, 12.3467655, -0.9874339, -21.625853, 13.784074, 0.11351812, -20.900877, 10.349287, 0.97084606, -20.331432, 12.314311, 1.342185, -21.160097, 12.3467655, -0.9874339, -20.331432, 12.314311, 1.342185, -21.625853, 13.784074, 0.11351812, -21.160097, 12.3467655, -0.9874339, -21.649036, 10.824738, 3.0151107, -20.331432, 12.314311, 1.342185, -20.900877, 10.349287, 0.97084606, -21.649036, 10.824738, 3.0151107, -20.900877, 12.281435, 3.6722255, -20.331432, 12.314311, 1.342185, -20.331432, 12.314311, 1.342185, -21.160097, 14.278913, 1.713524, -21.625853, 13.784074, 0.11351812, -22.841454, 14.934341, 1.8374442, -21.625853, 13.784074, 0.11351812, -21.160097, 14.278913, 1.713524, -20.900877, 12.281435, 3.6722255, -21.160097, 14.278913, 1.713524, -20.331432, 12.314311, 1.342185, -22.98392, 14.268375, 3.5440893, -22.841454, 14.934341, 1.8374442, -21.160097, 14.278913, 1.713524, -20.900877, 12.281435, 3.6722255, -21.510363, 13.755413, 3.1213274, -21.160097, 14.278913, 1.713524, -22.98392, 14.268375, 3.5440893, -21.160097, 14.278913, 1.713524, -21.510363, 13.755413, 3.1213274, -24.61638, 10.892176, -0.880373, -23.412582, 9.69428, 0.8473475, -22.98392, 10.403238, -1.8582478, -24.61638, 10.892176, -0.880373, -25.781822, 10.349287, 0.97084606, -23.412582, 9.69428, 0.8473475, -22.98392, 10.403238, -1.8582478, -23.412582, 9.69428, 0.8473475, -20.900877, 10.349287, 0.97084606, -22.98392, 10.403238, -1.8582478, -20.900877, 10.349287, 0.97084606, -21.510363, 10.892176, -0.880373, -23.412582, 9.69428, 0.8473475, -21.649036, 10.824738, 3.0151107, -20.900877, 10.349287, 0.97084606, -23.412582, 9.69428, 0.8473475, -23.698359, 10.294915, 3.8003616, -21.649036, 10.824738, 3.0151107, -25.781822, 10.349287, 0.97084606, -23.698359, 10.294915, 3.8003616, -23.412582, 9.69428, 0.8473475, -25.781822, 10.349287, 0.97084606, -25.366648, 10.824738, 3.0151107, -23.698359, 10.294915, 3.8003616, -20.253035, 9.625153, -3.2251654, -20.752932, 10.590384, -1.6858549, -20.401823, 9.271516, -2.002821, -20.401823, 9.271516, -2.002821, -20.752932, 10.590384, -1.6858549, -19.734592, 10.597971, -0.2561345, -20.401823, 9.271516, -2.002821, -19.734592, 10.597971, -0.2561345, -19.474949, 9.625154, -0.8681493, -20.253035, 9.625153, -3.2251654, -20.756725, 10.597969, -3.3503504, -20.752932, 10.590384, -1.6858549, -19.044178, 9.307764, -3.9160008, -20.756725, 10.597969, -3.3503504, -20.253035, 9.625153, -3.2251654, -20.752932, 10.590384, -1.6858549, -19.513306, 11.581747, -0.75813866, -19.734592, 10.597971, -0.2561345, -19.044178, 9.307764, -3.9160008, -19.309301, 10.619466, -3.9273815, -20.756725, 10.597969, -3.3503504, -17.62668, 9.670253, -3.5648928, -19.309301, 10.619466, -3.9273815, -19.044178, 9.307764, -3.9160008, -20.756725, 10.597969, -3.3503504, -20.089493, 11.924424, -1.6036634, -20.752932, 10.590384, -1.6858549, -20.752932, 10.590384, -1.6858549, -20.089493, 11.924424, -1.6036634, -19.513306, 11.581747, -0.75813866, -19.309301, 10.619466, -3.9273815, -20.163677, 11.581746, -2.7269554, -20.756725, 10.597969, -3.3503504, -20.756725, 10.597969, -3.3503504, -20.163677, 11.581746, -2.7269554, -20.089493, 11.924424, -1.6036634, -17.62668, 9.670253, -3.5648928, -17.569357, 10.641384, -3.6184235, -19.309301, 10.619466, -3.9273815, -17.569357, 10.641384, -3.6184235, -17.62668, 9.670253, -3.5648928, -16.665245, 9.344013, -2.7349634, -19.309301, 10.619466, -3.9273815, -19.281483, 11.931589, -3.052774, -20.163677, 11.581746, -2.7269554, -17.569357, 10.641384, -3.6184235, -19.281483, 11.931589, -3.052774, -19.309301, 10.619466, -3.9273815, -20.163677, 11.581746, -2.7269554, -18.978003, 12.368683, -1.870472, -20.089493, 11.924424, -1.6036634, -20.163677, 11.581746, -2.7269554, -19.281483, 11.931589, -3.052774, -18.978003, 12.368683, -1.870472, -17.569357, 10.641384, -3.6184235, -18.15271, 11.601134, -3.0422359, -19.281483, 11.931589, -3.052774, -17.569357, 10.641384, -3.6184235, -16.665245, 9.344013, -2.7349634, -16.69475, 10.64855, -2.6245313, -18.15271, 11.601134, -3.0422359, -17.569357, 10.641384, -3.6184235, -16.69475, 10.64855, -2.6245313, -16.69475, 10.64855, -2.6245313, -16.665245, 9.344013, -2.7349634, -16.976309, 9.670254, -1.596076, -19.281483, 11.931589, -3.052774, -18.15271, 11.601134, -3.0422359, -17.739222, 11.938755, -2.279746, -18.15271, 11.601134, -3.0422359, -16.69475, 10.64855, -2.6245313, -17.739222, 11.938755, -2.279746, -19.281483, 11.931589, -3.052774, -17.739222, 11.938755, -2.279746, -18.978003, 12.368683, -1.870472, -16.69475, 10.64855, -2.6245313, -16.976309, 9.670254, -1.596076, -16.835108, 10.641384, -1.396286, -17.739222, 11.938755, -2.279746, -16.69475, 10.64855, -2.6245313, -16.835108, 10.641384, -1.396286, -18.022047, 9.307765, -0.821784, -16.835108, 10.641384, -1.396286, -16.976309, 9.670254, -1.596076, -18.978003, 12.368683, -1.870472, -17.739222, 11.938755, -2.279746, -17.630474, 11.601135, -1.4607759, -17.739222, 11.938755, -2.279746, -16.835108, 10.641384, -1.396286, -17.630474, 11.601135, -1.4607759, -18.022047, 9.307765, -0.821784, -18.138802, 10.619467, -0.38300514, -16.835108, 10.641384, -1.396286, -18.138802, 10.619467, -0.38300514, -17.630474, 11.601135, -1.4607759, -16.835108, 10.641384, -1.396286, -19.474949, 9.625154, -0.8681493, -18.138802, 10.619467, -0.38300514, -18.022047, 9.307765, -0.821784, -19.474949, 9.625154, -0.8681493, -19.734592, 10.597971, -0.2561345, -18.138802, 10.619467, -0.38300514, -18.138802, 10.619467, -0.38300514, -18.547232, 11.93159, -0.8306365, -17.630474, 11.601135, -1.4607759, -18.978003, 12.368683, -1.870472, -17.630474, 11.601135, -1.4607759, -18.547232, 11.93159, -0.8306365, -19.734592, 10.597971, -0.2561345, -18.547232, 11.93159, -0.8306365, -18.138802, 10.619467, -0.38300514, -20.089493, 11.924424, -1.6036634, -18.978003, 12.368683, -1.870472, -18.547232, 11.93159, -0.8306365, -19.734592, 10.597971, -0.2561345, -19.513306, 11.581747, -0.75813866, -18.547232, 11.93159, -0.8306365, -20.089493, 11.924424, -1.6036634, -18.547232, 11.93159, -0.8306365, -19.513306, 11.581747, -0.75813866, -17.62668, 9.670253, -3.5648928, -18.469677, 8.87025, -2.4403362, -16.665245, 9.344013, -2.7349634, -17.62668, 9.670253, -3.5648928, -19.044178, 9.307764, -3.9160008, -18.469677, 8.87025, -2.4403362, -16.665245, 9.344013, -2.7349634, -18.469677, 8.87025, -2.4403362, -18.022047, 9.307765, -0.821784, -16.665245, 9.344013, -2.7349634, -18.022047, 9.307765, -0.821784, -16.976309, 9.670254, -1.596076, -18.469677, 8.87025, -2.4403362, -19.474949, 9.625154, -0.8681493, -18.022047, 9.307765, -0.821784, -18.469677, 8.87025, -2.4403362, -20.401823, 9.271516, -2.002821, -19.474949, 9.625154, -0.8681493, -19.044178, 9.307764, -3.9160008, -20.401823, 9.271516, -2.002821, -18.469677, 8.87025, -2.4403362, -19.044178, 9.307764, -3.9160008, -20.253035, 9.625153, -3.2251654, -20.401823, 9.271516, -2.002821, -17.464405, 7.537475, 1.8374468, -19.955458, 8.19459, 1.3919232, -17.577787, 7.2154503, 1.3678982, -17.464405, 7.537475, 1.8374468, -19.75946, 8.452126, 1.8374465, -19.955458, 8.19459, 1.3919232, -19.75946, 8.452126, 1.8374465, -22.311209, 10.6898575, 1.7080457, -19.955458, 8.19459, 1.3919232, -19.75946, 8.452126, 1.8374465, -22.217215, 10.689015, 1.8374457, -22.311209, 10.6898575, 1.7080457, -19.955458, 8.19459, 1.3919232, -22.311209, 10.6898575, 1.7080457, -22.46337, 10.691122, 1.757361, -17.577787, 7.2154503, 1.3678982, -19.955458, 8.19459, 1.3919232, -20.272423, 7.7777286, 1.5622085, -19.955458, 8.19459, 1.3919232, -22.46337, 10.691122, 1.757361, -20.272423, 7.7777286, 1.5622085, -17.577787, 7.2154503, 1.3678982, -20.272423, 7.7777286, 1.5622085, -17.761982, 6.6949005, 1.547035, -20.272423, 7.7777286, 1.5622085, -22.46337, 10.691122, 1.757361, -22.46337, 10.691122, 1.9175303, -17.761982, 6.6949005, 1.547035, -20.272423, 7.7777286, 1.5622085, -20.272423, 7.777729, 2.1126847, -20.272423, 7.7777286, 1.5622085, -22.46337, 10.691122, 1.9175303, -20.272423, 7.777729, 2.1126847, -17.761982, 6.6949005, 1.547035, -20.272423, 7.777729, 2.1126847, -17.761982, 6.694901, 2.127859, -20.272423, 7.777729, 2.1126847, -22.46337, 10.691122, 1.9175303, -22.311209, 10.689858, 1.9668455, -17.761982, 6.694901, 2.127859, -20.272423, 7.777729, 2.1126847, -19.955458, 8.194591, 2.28297, -20.272423, 7.777729, 2.1126847, -22.311209, 10.689858, 1.9668455, -19.955458, 8.194591, 2.28297, -19.955458, 8.194591, 2.28297, -22.311209, 10.689858, 1.9668455, -22.217215, 10.689015, 1.8374457, -17.761982, 6.694901, 2.127859, -19.955458, 8.194591, 2.28297, -17.577787, 7.215451, 2.3069956, -19.955458, 8.194591, 2.28297, -22.217215, 10.689015, 1.8374457, -19.75946, 8.452126, 1.8374465, -17.577787, 7.215451, 2.3069956, -19.955458, 8.194591, 2.28297, -19.75946, 8.452126, 1.8374465, -17.577787, 7.215451, 2.3069956, -19.75946, 8.452126, 1.8374465, -17.464405, 7.537475, 1.8374468, -19.772528, 3.1467314, 1.8374484, -17.801182, 7.205756, 2.2829702, -18.92911, 3.1467319, 3.0694866, -19.772528, 3.1467314, 1.8374484, -18.119411, 7.263501, 1.8374469, -17.801182, 7.205756, 2.2829702, -18.119411, 7.263501, 1.8374469, -17.641012, 11.282906, 2.034285, -17.801182, 7.205756, 2.2829702, -18.119411, 7.263501, 1.8374469, -17.783056, 11.2980795, 1.8374455, -17.641012, 11.282906, 2.034285, -17.801182, 7.205756, 2.2829702, -17.641012, 11.282906, 2.034285, -17.410873, 11.258881, 1.9592583, -18.92911, 3.1467319, 3.0694866, -17.801182, 7.205756, 2.2829702, -17.28611, 7.1121836, 2.1126852, -17.801182, 7.205756, 2.2829702, -17.410873, 11.258881, 1.9592583, -17.28611, 7.1121836, 2.1126852, -18.92911, 3.1467319, 3.0694866, -17.28611, 7.1121836, 2.1126852, -17.563877, 3.1463103, 2.599095, -17.28611, 7.1121836, 2.1126852, -17.410873, 11.258881, 1.9592583, -17.410873, 11.25888, 1.7156326, -17.563877, 3.1463103, 2.599095, -17.28611, 7.1121836, 2.1126852, -17.28611, 7.1121836, 1.5622088, -17.28611, 7.1121836, 2.1126852, -17.410873, 11.25888, 1.7156326, -17.28611, 7.1121836, 1.5622088, -17.563877, 3.1463103, 2.599095, -17.28611, 7.1121836, 1.5622088, -17.563877, 3.1463099, 1.0758016, -17.28611, 7.1121836, 1.5622088, -17.410873, 11.25888, 1.7156326, -17.641012, 11.282906, 1.6406059, -17.563877, 3.1463099, 1.0758016, -17.28611, 7.1121836, 1.5622088, -17.801182, 7.205756, 1.3919237, -17.28611, 7.1121836, 1.5622088, -17.641012, 11.282906, 1.6406059, -17.801182, 7.205756, 1.3919237, -17.801182, 7.205756, 1.3919237, -17.641012, 11.282906, 1.6406059, -17.783056, 11.2980795, 1.8374455, -17.563877, 3.1463099, 1.0758016, -17.801182, 7.205756, 1.3919237, -18.92911, 3.146731, 0.60541, -17.801182, 7.205756, 1.3919237, -17.783056, 11.2980795, 1.8374455, -18.119411, 7.263501, 1.8374469, -18.92911, 3.146731, 0.60541, -17.801182, 7.205756, 1.3919237, -18.119411, 7.263501, 1.8374469, -18.92911, 3.146731, 0.60541, -18.119411, 7.263501, 1.8374469, -19.772528, 3.1467314, 1.8374484, -17.85176, 7.430414, 1.5381833, -18.091171, 7.869193, -0.13432074, -17.577787, 7.2154503, 1.3678982, -17.85176, 7.430414, 1.5381833, -18.33269, 8.041164, 0.08317232, -18.091171, 7.869193, -0.13432074, -18.33269, 8.041164, 0.08317232, -18.784958, 9.534952, -1.5615134, -18.091171, 7.869193, -0.13432074, -18.33269, 8.041164, 0.08317232, -18.84734, 9.534531, -1.4746847, -18.784958, 9.534952, -1.5615134, -18.091171, 7.869193, -0.13432074, -18.784958, 9.534952, -1.5615134, -18.848183, 9.535795, -1.6474986, -17.577787, 7.2154503, 1.3678982, -18.091171, 7.869193, -0.13432074, -18.265673, 7.5910044, -0.29954767, -18.091171, 7.869193, -0.13432074, -18.848183, 9.535795, -1.6474986, -18.265673, 7.5910044, -0.29954767, -17.577787, 7.2154503, 1.3678982, -18.265673, 7.5910044, -0.29954767, -17.73037, 6.867715, 1.2886567, -18.265673, 7.5910044, -0.29954767, -18.848183, 9.535795, -1.6474986, -18.94934, 9.535795, -1.6142006, -17.73037, 6.867715, 1.2886567, -18.265673, 7.5910044, -0.29954767, -18.614672, 7.591005, -0.18447876, -18.265673, 7.5910044, -0.29954767, -18.94934, 9.535795, -1.6142006, -18.614672, 7.591005, -0.18447876, -17.73037, 6.867715, 1.2886567, -18.614672, 7.591005, -0.18447876, -18.098337, 6.867715, 1.4100481, -18.614672, 7.591005, -0.18447876, -18.94934, 9.535795, -1.6142006, -18.94892, 9.534952, -1.5075612, -18.098337, 6.867715, 1.4100481, -18.614672, 7.591005, -0.18447876, -18.65598, 7.869193, 0.052403092, -18.614672, 7.591005, -0.18447876, -18.94892, 9.534952, -1.5075612, -18.65598, 7.869193, 0.052403092, -18.65598, 7.869193, 0.052403092, -18.94892, 9.534952, -1.5075612, -18.84734, 9.534531, -1.4746847, -18.098337, 6.867715, 1.4100481, -18.65598, 7.869193, 0.052403092, -18.173365, 7.2154503, 1.5643163, -18.65598, 7.869193, 0.052403092, -18.84734, 9.534531, -1.4746847, -18.33269, 8.041164, 0.08317232, -18.173365, 7.2154503, 1.5643163, -18.65598, 7.869193, 0.052403092, -18.33269, 8.041164, 0.08317232, -18.173365, 7.2154503, 1.5643163, -18.33269, 8.041164, 0.08317232, -17.85176, 7.430414, 1.5381833, -18.021717, 16.363804, -17.998592, -21.997292, 20.020512, -13.591122, -21.163078, 14.983807, -13.799868, -21.163078, 14.983807, -13.799868, -21.997292, 20.020512, -13.591122, -20.16242, 20.148884, -7.004968, -21.163078, 14.983807, -13.799868, -20.16242, 20.148884, -7.004968, -19.366419, 16.295841, -7.970547, -18.021717, 16.363804, -17.998592, -19.619282, 19.941853, -19.07104, -21.997292, 20.020512, -13.591122, -11.484028, 15.332424, -18.699127, -19.619282, 19.941853, -19.07104, -18.021717, 16.363804, -17.998592, -21.997292, 20.020512, -13.591122, -18.141577, 23.844606, -8.429464, -20.16242, 20.148884, -7.004968, -11.484028, 15.332424, -18.699127, -11.858614, 20.29928, -19.150177, -19.619282, 19.941853, -19.07104, -6.256456, 16.73004, -14.795372, -11.858614, 20.29928, -19.150177, -11.484028, 15.332424, -18.699127, -19.619282, 19.941853, -19.07104, -18.61851, 25.10693, -12.27676, -21.997292, 20.020512, -13.591122, -21.997292, 20.020512, -13.591122, -18.61851, 25.10693, -12.27676, -18.141577, 23.844606, -8.429464, -11.858614, 20.29928, -19.150177, -17.480486, 23.753365, -16.033627, -19.619282, 19.941853, -19.07104, -19.619282, 19.941853, -19.07104, -17.480486, 23.753365, -16.033627, -18.61851, 25.10693, -12.27676, -6.256456, 16.73004, -14.795372, -4.954714, 20.567348, -15.343607, -11.858614, 20.29928, -19.150177, -4.954714, 20.567348, -15.343607, -6.256456, 16.73004, -14.795372, -5.1436496, 15.231738, -9.215076, -11.858614, 20.29928, -19.150177, -13.089849, 25.17615, -15.716137, -17.480486, 23.753365, -16.033627, -4.954714, 20.567348, -15.343607, -13.089849, 25.17615, -15.716137, -11.858614, 20.29928, -19.150177, -17.480486, 23.753365, -16.033627, -14.129517, 26.802193, -11.209101, -18.61851, 25.10693, -12.27676, -17.480486, 23.753365, -16.033627, -13.089849, 25.17615, -15.716137, -14.129517, 26.802193, -11.209101, -4.954714, 20.567348, -15.343607, -8.721195, 24.030872, -13.957819, -13.089849, 25.17615, -15.716137, -4.954714, 20.567348, -15.343607, -5.1436496, 15.231738, -9.215076, -5.146767, 20.21873, -9.1676445, -8.721195, 24.030872, -13.957819, -4.954714, 20.567348, -15.343607, -5.146767, 20.21873, -9.1676445, -5.146767, 20.21873, -9.1676445, -5.1436496, 15.231738, -9.215076, -9.403631, 16.237947, -5.131659, -13.089849, 25.17615, -15.716137, -8.721195, 24.030872, -13.957819, -9.362032, 25.156012, -10.226952, -8.721195, 24.030872, -13.957819, -5.146767, 20.21873, -9.1676445, -9.362032, 25.156012, -10.226952, -13.089849, 25.17615, -15.716137, -9.362032, 25.156012, -10.226952, -14.129517, 26.802193, -11.209101, -5.146767, 20.21873, -9.1676445, -9.403631, 16.237947, -5.131659, -9.5517025, 19.8204, -4.097917, -9.362032, 25.156012, -10.226952, -5.146767, 20.21873, -9.1676445, -9.5517025, 19.8204, -4.097917, -14.8233185, 14.882492, -4.315932, -9.5517025, 19.8204, -4.097917, -9.403631, 16.237947, -5.131659, -14.129517, 26.802193, -11.209101, -9.362032, 25.156012, -10.226952, -11.184143, 23.699244, -6.717871, -9.362032, 25.156012, -10.226952, -9.5517025, 19.8204, -4.097917, -11.184143, 23.699244, -6.717871, -14.8233185, 14.882492, -4.315932, -15.285444, 19.939335, -3.6085896, -9.5517025, 19.8204, -4.097917, -15.285444, 19.939335, -3.6085896, -11.184143, 23.699244, -6.717871, -9.5517025, 19.8204, -4.097917, -19.366419, 16.295841, -7.970547, -15.285444, 19.939335, -3.6085896, -14.8233185, 14.882492, -4.315932, -19.366419, 16.295841, -7.970547, -20.16242, 20.148884, -7.004968, -15.285444, 19.939335, -3.6085896, -15.285444, 19.939335, -3.6085896, -14.890806, 25.086163, -6.7869554, -11.184143, 23.699244, -6.717871, -14.129517, 26.802193, -11.209101, -11.184143, 23.699244, -6.717871, -14.890806, 25.086163, -6.7869554, -20.16242, 20.148884, -7.004968, -14.890806, 25.086163, -6.7869554, -15.285444, 19.939335, -3.6085896, -18.61851, 25.10693, -12.27676, -14.129517, 26.802193, -11.209101, -14.890806, 25.086163, -6.7869554, -20.16242, 20.148884, -7.004968, -18.141577, 23.844606, -8.429464, -14.890806, 25.086163, -6.7869554, -18.61851, 25.10693, -12.27676, -14.890806, 25.086163, -6.7869554, -18.141577, 23.844606, -8.429464, -6.256456, 16.73004, -14.795372, -13.014427, 13.437051, -11.550283, -5.1436496, 15.231738, -9.215076, -6.256456, 16.73004, -14.795372, -11.484028, 15.332424, -18.699127, -13.014427, 13.437051, -11.550283, -5.1436496, 15.231738, -9.215076, -13.014427, 13.437051, -11.550283, -14.8233185, 14.882492, -4.315932, -5.1436496, 15.231738, -9.215076, -14.8233185, 14.882492, -4.315932, -9.403631, 16.237947, -5.131659, -13.014427, 13.437051, -11.550283, -19.366419, 16.295841, -7.970547, -14.8233185, 14.882492, -4.315932, -13.014427, 13.437051, -11.550283, -21.163078, 14.983807, -13.799868, -19.366419, 16.295841, -7.970547, -11.484028, 15.332424, -18.699127, -21.163078, 14.983807, -13.799868, -13.014427, 13.437051, -11.550283, -11.484028, 15.332424, -18.699127, -18.021717, 16.363804, -17.998592, -21.163078, 14.983807, -13.799868, -13.562267, 14.716995, -23.311634, -16.202703, 16.876032, -20.837955, -15.172899, 13.925998, -21.079226, -15.172899, 13.925998, -21.079226, -16.202703, 16.876032, -20.837955, -15.753397, 16.891764, -16.938854, -15.172899, 13.925998, -21.079226, -15.753397, 16.891764, -16.938854, -14.583576, 14.716994, -17.856216, -13.562267, 14.716995, -23.311634, -14.412493, 16.891764, -24.101406, -16.202703, 16.876032, -20.837955, -10.448349, 14.0071745, -23.35928, -14.412493, 16.891764, -24.101406, -13.562267, 14.716995, -23.311634, -16.202703, 16.876032, -20.837955, -14.777542, 19.092333, -17.681902, -15.753397, 16.891764, -16.938854, -10.448349, 14.0071745, -23.35928, -10.954712, 16.940847, -23.667263, -14.412493, 16.891764, -24.101406, -8.051947, 14.8176775, -21.140478, -10.954712, 16.940847, -23.667263, -10.448349, 14.0071745, -23.35928, -14.412493, 16.891764, -24.101406, -14.993107, 19.85816, -19.960417, -16.202703, 16.876032, -20.837955, -16.202703, 16.876032, -20.837955, -14.993107, 19.85816, -19.960417, -14.777542, 19.092333, -17.681902, -10.954712, 16.940847, -23.667263, -13.92425, 19.092335, -22.239834, -14.412493, 16.891764, -24.101406, -14.412493, 16.891764, -24.101406, -13.92425, 19.092335, -22.239834, -14.993107, 19.85816, -19.960417, -8.051947, 14.8176775, -21.140478, -7.8814073, 16.989302, -21.182816, -10.954712, 16.940847, -23.667263, -7.8814073, 16.989302, -21.182816, -8.051947, 14.8176775, -21.140478, -7.065434, 14.08772, -18.476276, -10.954712, 16.940847, -23.667263, -11.844934, 19.873892, -21.924828, -13.92425, 19.092335, -22.239834, -7.8814073, 16.989302, -21.182816, -11.844934, 19.873892, -21.924828, -10.954712, 16.940847, -23.667263, -13.92425, 19.092335, -22.239834, -12.527824, 20.852411, -19.282501, -14.993107, 19.85816, -19.960417, -13.92425, 19.092335, -22.239834, -11.844934, 19.873892, -21.924828, -12.527824, 20.852411, -19.282501, -7.8814073, 16.989302, -21.182816, -9.646725, 19.135124, -20.685514, -11.844934, 19.873892, -21.924828, -7.8814073, 16.989302, -21.182816, -7.065434, 14.08772, -18.476276, -7.2431235, 17.005661, -18.293152, -9.646725, 19.135124, -20.685514, -7.8814073, 16.989302, -21.182816, -7.2431235, 17.005661, -18.293152, -7.2431235, 17.005661, -18.293152, -7.065434, 14.08772, -18.476276, -8.905237, 14.8176775, -16.582546, -11.844934, 19.873892, -21.924828, -9.646725, 19.135124, -20.685514, -9.66033, 19.890251, -18.745678, -9.646725, 19.135124, -20.685514, -7.2431235, 17.005661, -18.293152, -9.66033, 19.890251, -18.745678, -11.844934, 19.873892, -21.924828, -9.66033, 19.890251, -18.745678, -12.527824, 20.852411, -19.282501, -7.2431235, 17.005661, -18.293152, -8.905237, 14.8176775, -16.582546, -8.844357, 16.9893, -16.039139, -9.66033, 19.890251, -18.745678, -7.2431235, 17.005661, -18.293152, -8.844357, 16.9893, -16.039139, -11.789251, 14.007174, -16.196726, -8.844357, 16.9893, -16.039139, -8.905237, 14.8176775, -16.582546, -12.527824, 20.852411, -19.282501, -9.66033, 19.890251, -18.745678, -10.331999, 19.135124, -17.025066, -9.66033, 19.890251, -18.745678, -8.844357, 16.9893, -16.039139, -10.331999, 19.135124, -17.025066, -11.789251, 14.007174, -16.196726, -12.490612, 16.940847, -15.46311, -8.844357, 16.9893, -16.039139, -12.490612, 16.940847, -15.46311, -10.331999, 19.135124, -17.025066, -8.844357, 16.9893, -16.039139, -14.583576, 14.716994, -17.856216, -12.490612, 16.940847, -15.46311, -11.789251, 14.007174, -16.196726, -14.583576, 14.716994, -17.856216, -15.753397, 16.891764, -16.938854, -12.490612, 16.940847, -15.46311, -12.490612, 16.940847, -15.46311, -12.807882, 19.87389, -16.78115, -10.331999, 19.135124, -17.025066, -12.527824, 20.852411, -19.282501, -10.331999, 19.135124, -17.025066, -12.807882, 19.87389, -16.78115, -15.753397, 16.891764, -16.938854, -12.807882, 19.87389, -16.78115, -12.490612, 16.940847, -15.46311, -14.993107, 19.85816, -19.960417, -12.527824, 20.852411, -19.282501, -12.807882, 19.87389, -16.78115, -15.753397, 16.891764, -16.938854, -14.777542, 19.092333, -17.681902, -12.807882, 19.87389, -16.78115, -14.993107, 19.85816, -19.960417, -12.807882, 19.87389, -16.78115, -14.777542, 19.092333, -17.681902, -8.051947, 14.8176775, -21.140478, -10.918001, 13.029283, -19.848606, -7.065434, 14.08772, -18.476276, -8.051947, 14.8176775, -21.140478, -10.448349, 14.0071745, -23.35928, -10.918001, 13.029283, -19.848606, -7.065434, 14.08772, -18.476276, -10.918001, 13.029283, -19.848606, -11.789251, 14.007174, -16.196726, -7.065434, 14.08772, -18.476276, -11.789251, 14.007174, -16.196726, -8.905237, 14.8176775, -16.582546, -10.918001, 13.029283, -19.848606, -14.583576, 14.716994, -17.856216, -11.789251, 14.007174, -16.196726, -10.918001, 13.029283, -19.848606, -15.172899, 13.925998, -21.079226, -14.583576, 14.716994, -17.856216, -10.448349, 14.0071745, -23.35928, -15.172899, 13.925998, -21.079226, -10.918001, 13.029283, -19.848606, -10.448349, 14.0071745, -23.35928, -13.562267, 14.716995, -23.311634, -15.172899, 13.925998, -21.079226, -5.8097816, 12.926081, -14.093311, -7.931314, 14.367117, -15.249769, -7.562638, 12.39812, -14.647457, -7.562638, 12.39812, -14.647457, -7.931314, 14.367117, -15.249769, -10.30912, 14.378445, -14.14818, -7.562638, 12.39812, -14.647457, -10.30912, 14.378445, -14.14818, -9.4823475, 12.926081, -13.599033, -5.8097816, 12.926081, -14.093311, -5.4877048, 14.378444, -14.798061, -7.931314, 14.367117, -15.249769, -5.128114, 12.452237, -12.129587, -5.4877048, 14.378444, -14.798061, -5.8097816, 12.926081, -14.093311, -7.931314, 14.367117, -15.249769, -9.633245, 15.847169, -13.685543, -10.30912, 14.378445, -14.14818, -5.128114, 12.452237, -12.129587, -5.038579, 14.410537, -12.515514, -5.4877048, 14.378444, -14.798061, -6.032766, 12.993412, -10.145935, -5.038579, 14.410537, -12.515514, -5.128114, 12.452237, -12.129587, -5.4877048, 14.378444, -14.798061, -8.234188, 16.358768, -14.298784, -7.931314, 14.367117, -15.249769, -7.931314, 14.367117, -15.249769, -8.234188, 16.358768, -14.298784, -9.633245, 15.847169, -13.685543, -5.038579, 14.410537, -12.515514, -6.565431, 15.847169, -14.099052, -5.4877048, 14.378444, -14.798061, -5.4877048, 14.378444, -14.798061, -6.565431, 15.847169, -14.099052, -8.234188, 16.358768, -14.298784, -6.032766, 12.993412, -10.145935, -5.9699607, 14.443258, -10.047108, -5.038579, 14.410537, -12.515514, -5.9699607, 14.443258, -10.047108, -6.032766, 12.993412, -10.145935, -7.5147758, 12.506355, -8.963072, -5.038579, 14.410537, -12.515514, -6.3296666, 16.369465, -12.714966, -6.565431, 15.847169, -14.099052, -5.9699607, 14.443258, -10.047108, -6.3296666, 16.369465, -12.714966, -5.038579, 14.410537, -12.515514, -6.565431, 15.847169, -14.099052, -8.14801, 17.022022, -12.594429, -8.234188, 16.358768, -14.298784, -6.565431, 15.847169, -14.099052, -6.3296666, 16.369465, -12.714966, -8.14801, 17.022022, -12.594429, -5.9699607, 14.443258, -10.047108, -6.655229, 15.876115, -11.061441, -6.3296666, 16.369465, -12.714966, -5.9699607, 14.443258, -10.047108, -7.5147758, 12.506355, -8.963072, -7.6687236, 14.453957, -9.036707, -6.655229, 15.876115, -11.061441, -5.9699607, 14.443258, -10.047108, -7.6687236, 14.453957, -9.036707, -7.6687236, 14.453957, -9.036707, -7.5147758, 12.506355, -8.963072, -9.10058, 12.993413, -9.732424, -6.3296666, 16.369465, -12.714966, -6.655229, 15.876115, -11.061441, -7.8877406, 16.380163, -10.664139, -6.655229, 15.876115, -11.061441, -7.6687236, 14.453957, -9.036707, -7.8877406, 16.380163, -10.664139, -6.3296666, 16.369465, -12.714966, -7.8877406, 16.380163, -10.664139, -8.14801, 17.022022, -12.594429, -7.6687236, 14.453957, -9.036707, -9.10058, 12.993413, -9.732424, -9.432554, 14.443259, -9.580103, -7.8877406, 16.380163, -10.664139, -7.6687236, 14.453957, -9.036707, -9.432554, 14.443259, -9.580103, -9.94953, 12.452238, -11.479706, -9.432554, 14.443259, -9.580103, -9.10058, 12.993413, -9.732424, -8.14801, 17.022022, -12.594429, -7.8877406, 16.380163, -10.664139, -9.119415, 15.876115, -10.7295475, -7.8877406, 16.380163, -10.664139, -9.432554, 14.443259, -9.580103, -9.119415, 15.876115, -10.7295475, -9.94953, 12.452238, -11.479706, -10.561342, 14.410537, -11.77158, -9.432554, 14.443259, -9.580103, -10.561342, 14.410537, -11.77158, -9.119415, 15.876115, -10.7295475, -9.432554, 14.443259, -9.580103, -9.4823475, 12.926081, -13.599033, -10.561342, 14.410537, -11.77158, -9.94953, 12.452238, -11.479706, -9.4823475, 12.926081, -13.599033, -10.30912, 14.378445, -14.14818, -10.561342, 14.410537, -11.77158, -10.561342, 14.410537, -11.77158, -9.792261, 16.369465, -12.247959, -9.119415, 15.876115, -10.7295475, -8.14801, 17.022022, -12.594429, -9.119415, 15.876115, -10.7295475, -9.792261, 16.369465, -12.247959, -10.30912, 14.378445, -14.14818, -9.792261, 16.369465, -12.247959, -10.561342, 14.410537, -11.77158, -8.234188, 16.358768, -14.298784, -8.14801, 17.022022, -12.594429, -9.792261, 16.369465, -12.247959, -10.30912, 14.378445, -14.14818, -9.633245, 15.847169, -13.685543, -9.792261, 16.369465, -12.247959, -8.234188, 16.358768, -14.298784, -9.792261, 16.369465, -12.247959, -9.633245, 15.847169, -13.685543, -6.032766, 12.993412, -10.145935, -7.45141, 11.799051, -11.691931, -7.5147758, 12.506355, -8.963072, -6.032766, 12.993412, -10.145935, -5.128114, 12.452237, -12.129587, -7.45141, 11.799051, -11.691931, -7.5147758, 12.506355, -8.963072, -7.45141, 11.799051, -11.691931, -9.94953, 12.452238, -11.479706, -7.5147758, 12.506355, -8.963072, -9.94953, 12.452238, -11.479706, -9.10058, 12.993413, -9.732424, -7.45141, 11.799051, -11.691931, -9.4823475, 12.926081, -13.599033, -9.94953, 12.452238, -11.479706, -7.45141, 11.799051, -11.691931, -7.562638, 12.39812, -14.647457, -9.4823475, 12.926081, -13.599033, -5.128114, 12.452237, -12.129587, -7.562638, 12.39812, -14.647457, -7.45141, 11.799051, -11.691931, -5.128114, 12.452237, -12.129587, -5.8097816, 12.926081, -14.093311, -7.562638, 12.39812, -14.647457, -14.00502, 9.80929, -11.391943, -12.666889, 10.790327, -14.925047, -13.28483, 9.328525, -11.429331, -14.00502, 9.80929, -11.391943, -13.374517, 11.174813, -14.759827, -12.666889, 10.790327, -14.925047, -13.374517, 11.174813, -14.759827, -12.483606, 14.515627, -18.468845, -12.666889, 10.790327, -14.925047, -13.374517, 11.174813, -14.759827, -12.699318, 14.514368, -18.366463, -12.483606, 14.515627, -18.468845, -12.666889, 10.790327, -14.925047, -12.483606, 14.515627, -18.468845, -12.5141735, 14.517514, -18.70568, -13.28483, 9.328525, -11.429331, -12.666889, 10.790327, -14.925047, -12.829697, 10.167975, -15.43696, -12.666889, 10.790327, -14.925047, -12.5141735, 14.517514, -18.70568, -12.829697, 10.167975, -15.43696, -13.28483, 9.328525, -11.429331, -12.829697, 10.167975, -15.43696, -13.497103, 8.551372, -11.74884, -12.829697, 10.167975, -15.43696, -12.5141735, 14.517514, -18.70568, -12.749214, 14.517514, -18.749683, -13.497103, 8.551372, -11.74884, -12.829697, 10.167975, -15.43696, -13.637495, 10.167976, -15.588188, -12.829697, 10.167975, -15.43696, -12.749214, 14.517514, -18.749683, -13.637495, 10.167976, -15.588188, -13.497103, 8.551372, -11.74884, -13.637495, 10.167976, -15.588188, -14.349434, 8.551372, -11.908405, -13.637495, 10.167976, -15.588188, -12.749214, 14.517514, -18.749683, -12.863384, 14.515627, -18.539944, -14.349434, 8.551372, -11.908405, -13.637495, 10.167976, -15.588188, -13.974457, 10.790327, -15.169838, -13.637495, 10.167976, -15.588188, -12.863384, 14.515627, -18.539944, -13.974457, 10.790327, -15.169838, -13.974457, 10.790327, -15.169838, -12.863384, 14.515627, -18.539944, -12.699318, 14.514368, -18.366463, -14.349434, 8.551372, -11.908405, -13.974457, 10.790327, -15.169838, -14.662912, 9.328525, -11.687323, -13.974457, 10.790327, -15.169838, -12.699318, 14.514368, -18.366463, -13.374517, 11.174813, -14.759827, -14.662912, 9.328525, -11.687323, -13.974457, 10.790327, -15.169838, -13.374517, 11.174813, -14.759827, -14.662912, 9.328525, -11.687323, -13.374517, 11.174813, -14.759827, -14.00502, 9.80929, -11.391943, -13.370928, 3.2541475, -14.779001, -14.566284, 9.314051, -12.008541, -15.410591, 3.2541475, -13.879795, -13.370928, 3.2541475, -14.779001, -13.825075, 9.400261, -12.353134, -14.566284, 9.314051, -12.008541, -13.825075, 9.400261, -12.353134, -14.245354, 15.401014, -11.705181, -14.566284, 9.314051, -12.008541, -13.825075, 9.400261, -12.353134, -13.917479, 15.423668, -11.859549, -14.245354, 15.401014, -11.705181, -14.566284, 9.314051, -12.008541, -14.245354, 15.401014, -11.705181, -14.198481, 15.365146, -11.346853, -15.410591, 3.2541475, -13.879795, -14.566284, 9.314051, -12.008541, -14.457899, 9.174353, -11.205919, -14.566284, 9.314051, -12.008541, -14.198481, 15.365146, -11.346853, -14.457899, 9.174353, -11.205919, -15.410591, 3.2541475, -13.879795, -14.457899, 9.174353, -11.205919, -15.095373, 3.253518, -11.747156, -14.457899, 9.174353, -11.205919, -14.198481, 15.365146, -11.346853, -13.840971, 15.365146, -11.279924, -15.095373, 3.253518, -11.747156, -14.457899, 9.174353, -11.205919, -13.650103, 9.174353, -11.054691, -14.457899, 9.174353, -11.205919, -13.840971, 15.365146, -11.279924, -13.650103, 9.174353, -11.054691, -15.095373, 3.253518, -11.747156, -13.650103, 9.174353, -11.054691, -12.860012, 3.253518, -11.328674, -13.650103, 9.174353, -11.054691, -13.840971, 15.365146, -11.279924, -13.667648, 15.401014, -11.597029, -12.860012, 3.253518, -11.328674, -13.650103, 9.174353, -11.054691, -13.258715, 9.314051, -11.763752, -13.650103, 9.174353, -11.054691, -13.667648, 15.401014, -11.597029, -13.258715, 9.314051, -11.763752, -13.258715, 9.314051, -11.763752, -13.667648, 15.401014, -11.597029, -13.917479, 15.423668, -11.859549, -12.860012, 3.253518, -11.328674, -13.258715, 9.314051, -11.763752, -11.794676, 3.2541475, -13.20286, -13.258715, 9.314051, -11.763752, -13.917479, 15.423668, -11.859549, -13.825075, 9.400261, -12.353134, -11.794676, 3.2541475, -13.20286, -13.258715, 9.314051, -11.763752, -13.825075, 9.400261, -12.353134, -11.794676, 3.2541475, -13.20286, -13.825075, 9.400261, -12.353134, -13.370928, 3.2541475, -14.779001, -13.45945, 9.649454, -11.878155, -10.93936, 10.304527, -11.770006, -13.28483, 9.328525, -11.429331, -13.45945, 9.649454, -11.878155, -11.192169, 10.561271, -12.184172, -10.93936, 10.304527, -11.770006, -11.192169, 10.561271, -12.184172, -8.654425, 12.791416, -12.396021, -10.93936, 10.304527, -11.770006, -11.192169, 10.561271, -12.184172, -8.764704, 12.790787, -12.511417, -8.654425, 12.791416, -12.396021, -10.93936, 10.304527, -11.770006, -8.654425, 12.791416, -12.396021, -8.510876, 12.792675, -12.465178, -13.28483, 9.328525, -11.429331, -10.93936, 10.304527, -11.770006, -10.648958, 9.889207, -11.980684, -10.93936, 10.304527, -11.770006, -8.510876, 12.792675, -12.465178, -10.648958, 9.889207, -11.980684, -13.28483, 9.328525, -11.429331, -10.648958, 9.889207, -11.980684, -13.126629, 8.809374, -11.63147, -10.648958, 9.889207, -11.980684, -8.510876, 12.792675, -12.465178, -8.53195, 12.792675, -12.622772, -13.126629, 8.809374, -11.63147, -10.648958, 9.889207, -11.980684, -10.721937, 9.889207, -12.524437, -10.648958, 9.889207, -11.980684, -8.53195, 12.792675, -12.622772, -10.721937, 9.889207, -12.524437, -13.126629, 8.809374, -11.63147, -10.721937, 9.889207, -12.524437, -13.203676, 8.809374, -12.204792, -10.721937, 9.889207, -12.524437, -8.53195, 12.792675, -12.622772, -8.688553, 12.791416, -12.651451, -13.203676, 8.809374, -12.204792, -10.721937, 9.889207, -12.524437, -11.058203, 10.304527, -12.650129, -10.721937, 9.889207, -12.524437, -8.688553, 12.791416, -12.651451, -11.058203, 10.304527, -12.650129, -11.058203, 10.304527, -12.650129, -8.688553, 12.791416, -12.651451, -8.764704, 12.790787, -12.511417, -13.203676, 8.809374, -12.204792, -11.058203, 10.304527, -12.650129, -13.409447, 9.328525, -12.357271, -11.058203, 10.304527, -12.650129, -8.764704, 12.790787, -12.511417, -11.192169, 10.561271, -12.184172, -13.409447, 9.328525, -12.357271, -11.058203, 10.304527, -12.650129, -11.192169, 10.561271, -12.184172, -13.409447, 9.328525, -12.357271, -11.192169, 10.561271, -12.184172, -13.45945, 9.649454, -11.878155, 1.2086613, -17.361609, 9.414333, 2.631641, -16.33421, 10.284525, 2.5310369, -17.7495, 10.064947, 2.5310369, -17.7495, 10.064947, 2.631641, -16.33421, 10.284525, 4.3560047, -16.298113, 9.437187, 2.5310369, -17.7495, 10.064947, 4.3560047, -16.298113, 9.437187, 4.0482125, -17.380833, 9.267164, 1.2086613, -17.361609, 9.414333, 0.99485135, -16.35621, 9.910944, 2.631641, -16.33421, 10.284525, 0.67701983, -17.65146, 7.644686, 0.99485135, -16.35621, 9.910944, 1.2086613, -17.361609, 9.414333, 2.631641, -16.33421, 10.284525, 3.8578982, -15.259179, 8.952537, 4.3560047, -16.298113, 9.437187, 0.67701983, -17.65146, 7.644686, 0.57171667, -16.255608, 7.7713485, 0.99485135, -16.35621, 9.910944, 1.4848416, -17.258871, 5.9989257, 0.57171667, -16.255608, 7.7713485, 0.67701983, -17.65146, 7.644686, 0.99485135, -16.35621, 9.910944, 2.820033, -14.904823, 9.283184, 2.631641, -16.33421, 10.284525, 2.631641, -16.33421, 10.284525, 2.820033, -14.904823, 9.283184, 3.8578982, -15.259179, 8.952537, 0.57171667, -16.255608, 7.7713485, 1.7232151, -15.285238, 9.163143, 0.99485135, -16.35621, 9.910944, 0.99485135, -16.35621, 9.910944, 1.7232151, -15.285238, 9.163143, 2.820033, -14.904823, 9.283184, 1.4848416, -17.258871, 5.9989257, 1.2661188, -16.180422, 5.6674232, 0.57171667, -16.255608, 7.7713485, 1.2661188, -16.180422, 5.6674232, 1.4848416, -17.258871, 5.9989257, 2.9689097, -17.679869, 5.4027777, 0.57171667, -16.255608, 7.7713485, 1.5839503, -14.885172, 7.933895, 1.7232151, -15.285238, 9.163143, 1.2661188, -16.180422, 5.6674232, 1.5839503, -14.885172, 7.933895, 0.57171667, -16.255608, 7.7713485, 1.7232151, -15.285238, 9.163143, 2.8828304, -14.428289, 7.988148, 2.820033, -14.904823, 9.283184, 1.7232151, -15.285238, 9.163143, 1.5839503, -14.885172, 7.933895, 2.8828304, -14.428289, 7.988148, 1.2661188, -16.180422, 5.6674232, 1.8436836, -15.206849, 6.6362967, 1.5839503, -14.885172, 7.933895, 1.2661188, -16.180422, 5.6674232, 2.9689097, -17.679869, 5.4027777, 2.9821527, -16.278463, 5.4012823, 1.8436836, -15.206849, 6.6362967, 1.2661188, -16.180422, 5.6674232, 2.9821527, -16.278463, 5.4012823, 2.9821527, -16.278463, 5.4012823, 2.9689097, -17.679869, 5.4027777, 4.3171306, -17.397068, 6.368234, 1.5839503, -14.885172, 7.933895, 1.8436836, -15.206849, 6.6362967, 2.9076076, -14.890726, 6.620277, 1.8436836, -15.206849, 6.6362967, 2.9821527, -16.278463, 5.4012823, 2.9076076, -14.890726, 6.620277, 1.5839503, -14.885172, 7.933895, 2.9076076, -14.890726, 6.620277, 2.8828304, -14.428289, 7.988148, 2.9821527, -16.278463, 5.4012823, 4.3171306, -17.397068, 6.368234, 4.6103983, -16.390173, 6.355845, 2.9076076, -14.890726, 6.620277, 2.9821527, -16.278463, 5.4012823, 4.6103983, -16.390173, 6.355845, 4.8227134, -17.777908, 7.823253, 4.6103983, -16.390173, 6.355845, 4.3171306, -17.397068, 6.368234, 2.8828304, -14.428289, 7.988148, 2.9076076, -14.890726, 6.620277, 3.9711041, -15.30019, 6.942167, 2.9076076, -14.890726, 6.620277, 4.6103983, -16.390173, 6.355845, 3.9711041, -15.30019, 6.942167, 4.8227134, -17.777908, 7.823253, 5.042077, -16.356638, 7.9142447, 4.6103983, -16.390173, 6.355845, 5.042077, -16.356638, 7.9142447, 3.9711041, -15.30019, 6.942167, 4.6103983, -16.390173, 6.355845, 4.0482125, -17.380833, 9.267164, 5.042077, -16.356638, 7.9142447, 4.8227134, -17.777908, 7.823253, 4.0482125, -17.380833, 9.267164, 4.3560047, -16.298113, 9.437187, 5.042077, -16.356638, 7.9142447, 5.042077, -16.356638, 7.9142447, 4.1434765, -14.910377, 7.969779, 3.9711041, -15.30019, 6.942167, 2.8828304, -14.428289, 7.988148, 3.9711041, -15.30019, 6.942167, 4.1434765, -14.910377, 7.969779, 4.3560047, -16.298113, 9.437187, 4.1434765, -14.910377, 7.969779, 5.042077, -16.356638, 7.9142447, 2.820033, -14.904823, 9.283184, 2.8828304, -14.428289, 7.988148, 4.1434765, -14.910377, 7.969779, 4.3560047, -16.298113, 9.437187, 3.8578982, -15.259179, 8.952537, 4.1434765, -14.910377, 7.969779, 2.820033, -14.904823, 9.283184, 4.1434765, -14.910377, 7.969779, 3.8578982, -15.259179, 8.952537, 1.4848416, -17.258871, 5.9989257, 2.7309632, -18.18417, 7.6976585, 2.9689097, -17.679869, 5.4027777, 1.4848416, -17.258871, 5.9989257, 0.67701983, -17.65146, 7.644686, 2.7309632, -18.18417, 7.6976585, 2.9689097, -17.679869, 5.4027777, 2.7309632, -18.18417, 7.6976585, 4.8227134, -17.777908, 7.823253, 2.9689097, -17.679869, 5.4027777, 4.8227134, -17.777908, 7.823253, 4.3171306, -17.397068, 6.368234, 2.7309632, -18.18417, 7.6976585, 4.0482125, -17.380833, 9.267164, 4.8227134, -17.777908, 7.823253, 2.7309632, -18.18417, 7.6976585, 2.5310369, -17.7495, 10.064947, 4.0482125, -17.380833, 9.267164, 0.67701983, -17.65146, 7.644686, 2.5310369, -17.7495, 10.064947, 2.7309632, -18.18417, 7.6976585, 0.67701983, -17.65146, 7.644686, 1.2086613, -17.361609, 9.414333, 2.5310369, -17.7495, 10.064947, -1.1748617, -18.139956, 8.584938, -0.18462849, -17.407104, 9.311379, -0.3294468, -18.408445, 8.982868, -0.3294468, -18.408445, 8.982868, -0.18462849, -17.407104, 9.311379, 1.0884066, -17.401766, 8.917934, -0.3294468, -18.408445, 8.982868, 1.0884066, -17.401766, 8.917934, 0.70905924, -18.139956, 8.584938, -1.1748617, -18.139956, 8.584938, -1.3850405, -17.401766, 8.917934, -0.18462849, -17.407104, 9.311379, -1.3850405, -18.381105, 7.5489955, -1.3850405, -17.401766, 8.917934, -1.1748617, -18.139956, 8.584938, -0.18462849, -17.407104, 9.311379, 0.7793324, -16.655033, 8.638763, 1.0884066, -17.401766, 8.917934, -1.3850405, -18.381105, 7.5489955, -1.4563816, -17.385319, 7.7371736, -1.3850405, -17.401766, 8.917934, -0.79465985, -18.10578, 6.6108794, -1.4563816, -17.385319, 7.7371736, -1.3850405, -18.381105, 7.5489955, -1.3850405, -17.401766, 8.917934, 0.03259921, -16.395086, 8.853001, -0.18462849, -17.407104, 9.311379, -0.18462849, -17.407104, 9.311379, 0.03259921, -16.395086, 8.853001, 0.7793324, -16.655033, 8.638763, -1.4563816, -17.385319, 7.7371736, -0.79465985, -16.655033, 8.638763, -1.3850405, -17.401766, 8.917934, -1.3850405, -17.401766, 8.917934, -0.79465985, -16.655033, 8.638763, 0.03259921, -16.395086, 8.853001, -0.79465985, -18.10578, 6.6108794, -0.8192234, -17.368872, 6.556626, -1.4563816, -17.385319, 7.7371736, -0.8192234, -17.368872, 6.556626, -0.79465985, -18.10578, 6.6108794, 0.03259921, -18.353552, 6.115336, -1.4563816, -17.385319, 7.7371736, -0.8192234, -16.389532, 7.9253516, -0.79465985, -16.655033, 8.638763, -0.8192234, -17.368872, 6.556626, -0.8192234, -16.389532, 7.9253516, -1.4563816, -17.385319, 7.7371736, -0.79465985, -16.655033, 8.638763, 0.10500836, -16.057604, 7.9881487, 0.03259921, -16.395086, 8.853001, -0.79465985, -16.655033, 8.638763, -0.8192234, -16.389532, 7.9253516, 0.10500836, -16.057604, 7.9881487, -0.8192234, -17.368872, 6.556626, -0.54325676, -16.640295, 7.1145396, -0.8192234, -16.389532, 7.9253516, -0.8192234, -17.368872, 6.556626, 0.03259921, -18.353552, 6.115336, 0.10500836, -17.36332, 6.1631813, -0.54325676, -16.640295, 7.1145396, -0.8192234, -17.368872, 6.556626, 0.10500836, -17.36332, 6.1631813, 0.10500836, -17.36332, 6.1631813, 0.03259921, -18.353552, 6.115336, 0.7793324, -18.10578, 6.6108794, -0.8192234, -16.389532, 7.9253516, -0.54325676, -16.640295, 7.1145396, 0.10500836, -16.38398, 6.9979157, -0.54325676, -16.640295, 7.1145396, 0.10500836, -17.36332, 6.1631813, 0.10500836, -16.38398, 6.9979157, -0.8192234, -16.389532, 7.9253516, 0.10500836, -16.38398, 6.9979157, 0.10500836, -16.057604, 7.9881487, 0.10500836, -17.36332, 6.1631813, 0.7793324, -18.10578, 6.6108794, 0.9570446, -17.368872, 6.556626, 0.10500836, -16.38398, 6.9979157, 0.10500836, -17.36332, 6.1631813, 0.9570446, -17.368872, 6.556626, 1.0884066, -18.381105, 7.5489955, 0.9570446, -17.368872, 6.556626, 0.7793324, -18.10578, 6.6108794, 0.10500836, -16.057604, 7.9881487, 0.10500836, -16.38398, 6.9979157, 0.7210206, -16.640295, 7.1145396, 0.10500836, -16.38398, 6.9979157, 0.9570446, -17.368872, 6.556626, 0.7210206, -16.640295, 7.1145396, 1.0884066, -18.381105, 7.5489955, 1.3767617, -17.385319, 7.7371736, 0.9570446, -17.368872, 6.556626, 1.3767617, -17.385319, 7.7371736, 0.7210206, -16.640295, 7.1145396, 0.9570446, -17.368872, 6.556626, 0.70905924, -18.139956, 8.584938, 1.3767617, -17.385319, 7.7371736, 1.0884066, -18.381105, 7.5489955, 0.70905924, -18.139956, 8.584938, 1.0884066, -17.401766, 8.917934, 1.3767617, -17.385319, 7.7371736, 1.3767617, -17.385319, 7.7371736, 0.9570446, -16.389532, 7.9253516, 0.7210206, -16.640295, 7.1145396, 0.10500836, -16.057604, 7.9881487, 0.7210206, -16.640295, 7.1145396, 0.9570446, -16.389532, 7.9253516, 1.0884066, -17.401766, 8.917934, 0.9570446, -16.389532, 7.9253516, 1.3767617, -17.385319, 7.7371736, 0.03259921, -16.395086, 8.853001, 0.10500836, -16.057604, 7.9881487, 0.9570446, -16.389532, 7.9253516, 1.0884066, -17.401766, 8.917934, 0.7793324, -16.655033, 8.638763, 0.9570446, -16.389532, 7.9253516, 0.03259921, -16.395086, 8.853001, 0.9570446, -16.389532, 7.9253516, 0.7793324, -16.655033, 8.638763, -0.79465985, -18.10578, 6.6108794, -0.18462849, -18.712822, 7.4864116, 0.03259921, -18.353552, 6.115336, -0.79465985, -18.10578, 6.6108794, -1.3850405, -18.381105, 7.5489955, -0.18462849, -18.712822, 7.4864116, 0.03259921, -18.353552, 6.115336, -0.18462849, -18.712822, 7.4864116, 1.0884066, -18.381105, 7.5489955, 0.03259921, -18.353552, 6.115336, 1.0884066, -18.381105, 7.5489955, 0.7793324, -18.10578, 6.6108794, -0.18462849, -18.712822, 7.4864116, 0.70905924, -18.139956, 8.584938, 1.0884066, -18.381105, 7.5489955, -0.18462849, -18.712822, 7.4864116, -0.3294468, -18.408445, 8.982868, 0.70905924, -18.139956, 8.584938, -1.3850405, -18.381105, 7.5489955, -0.3294468, -18.408445, 8.982868, -0.18462849, -18.712822, 7.4864116, -1.3850405, -18.381105, 7.5489955, -1.1748617, -18.139956, 8.584938, -0.3294468, -18.408445, 8.982868, 2.6649618, -19.729753, 7.98815, 1.5668626, -19.439903, 7.791855, 2.6147668, -19.871582, 7.781175, 2.6649618, -19.729753, 7.98815, 1.6531556, -19.32627, 7.98815, 1.5668626, -19.439903, 7.791855, 1.6531556, -19.32627, 7.98815, 0.52835655, -18.340096, 7.9311194, 1.5668626, -19.439903, 7.791855, 1.6531556, -19.32627, 7.98815, 0.5697943, -18.34031, 7.9881496, 0.52835655, -18.340096, 7.9311194, 1.5668626, -19.439903, 7.791855, 0.52835655, -18.340096, 7.9311194, 0.46128738, -18.339455, 7.952906, 2.6147668, -19.871582, 7.781175, 1.5668626, -19.439903, 7.791855, 1.4271705, -19.62381, 7.8668275, 1.5668626, -19.439903, 7.791855, 0.46128738, -18.339455, 7.952906, 1.4271705, -19.62381, 7.8668275, 2.6147668, -19.871582, 7.781175, 1.4271705, -19.62381, 7.8668275, 2.5338137, -20.101198, 7.860206, 1.4271705, -19.62381, 7.8668275, 0.46128738, -18.339455, 7.952906, 0.46128738, -18.339455, 8.023394, 2.5338137, -20.101198, 7.860206, 1.4271705, -19.62381, 7.8668275, 1.4271705, -19.62381, 8.109472, 1.4271705, -19.62381, 7.8668275, 0.46128738, -18.339455, 8.023394, 1.4271705, -19.62381, 8.109472, 2.5338137, -20.101198, 7.860206, 1.4271705, -19.62381, 8.109472, 2.5338137, -20.101198, 8.116095, 1.4271705, -19.62381, 8.109472, 0.46128738, -18.339455, 8.023394, 0.52835655, -18.340096, 8.045179, 2.5338137, -20.101198, 8.116095, 1.4271705, -19.62381, 8.109472, 1.5668626, -19.439903, 8.184445, 1.4271705, -19.62381, 8.109472, 0.52835655, -18.340096, 8.045179, 1.5668626, -19.439903, 8.184445, 1.5668626, -19.439903, 8.184445, 0.52835655, -18.340096, 8.045179, 0.5697943, -18.34031, 7.9881496, 2.5338137, -20.101198, 8.116095, 1.5668626, -19.439903, 8.184445, 2.6147668, -19.871582, 8.195126, 1.5668626, -19.439903, 8.184445, 0.5697943, -18.34031, 7.9881496, 1.6531556, -19.32627, 7.98815, 2.6147668, -19.871582, 8.195126, 1.5668626, -19.439903, 8.184445, 1.6531556, -19.32627, 7.98815, 2.6147668, -19.871582, 8.195126, 1.6531556, -19.32627, 7.98815, 2.6649618, -19.729753, 7.98815, 1.5335416, -22.031042, 7.988151, 2.5325322, -19.973894, 8.213709, 1.9611616, -22.031042, 8.6124935, 1.5335416, -22.031042, 7.988151, 2.3712668, -19.944632, 7.98815, 2.5325322, -19.973894, 8.213709, 2.3712668, -19.944632, 7.98815, 2.613699, -17.907776, 8.087899, 2.5325322, -19.973894, 8.213709, 2.3712668, -19.944632, 7.98815, 2.5417168, -17.900085, 7.9881496, 2.613699, -17.907776, 8.087899, 2.5325322, -19.973894, 8.213709, 2.613699, -17.907776, 8.087899, 2.730536, -17.920164, 8.049879, 1.9611616, -22.031042, 8.6124935, 2.5325322, -19.973894, 8.213709, 2.7935472, -20.021313, 8.127628, 2.5325322, -19.973894, 8.213709, 2.730536, -17.920164, 8.049879, 2.7935472, -20.021313, 8.127628, 1.9611616, -22.031042, 8.6124935, 2.7935472, -20.021313, 8.127628, 2.652787, -22.031042, 8.373906, 2.7935472, -20.021313, 8.127628, 2.730536, -17.920164, 8.049879, 2.730536, -17.920164, 7.92642, 2.652787, -22.031042, 8.373906, 2.7935472, -20.021313, 8.127628, 2.7935472, -20.021313, 7.848672, 2.7935472, -20.021313, 8.127628, 2.730536, -17.920164, 7.92642, 2.7935472, -20.021313, 7.848672, 2.652787, -22.031042, 8.373906, 2.7935472, -20.021313, 7.848672, 2.652787, -22.031042, 7.6021824, 2.7935472, -20.021313, 7.848672, 2.730536, -17.920164, 7.92642, 2.613699, -17.907776, 7.8884, 2.652787, -22.031042, 7.6021824, 2.7935472, -20.021313, 7.848672, 2.5325322, -19.973894, 7.7623787, 2.7935472, -20.021313, 7.848672, 2.613699, -17.907776, 7.8884, 2.5325322, -19.973894, 7.7623787, 2.5325322, -19.973894, 7.7623787, 2.613699, -17.907776, 7.8884, 2.5417168, -17.900085, 7.9881496, 2.652787, -22.031042, 7.6021824, 2.5325322, -19.973894, 7.7623787, 1.9611616, -22.031042, 7.3638086, 2.5325322, -19.973894, 7.7623787, 2.5417168, -17.900085, 7.9881496, 2.3712668, -19.944632, 7.98815, 1.9611616, -22.031042, 7.3638086, 2.5325322, -19.973894, 7.7623787, 2.3712668, -19.944632, 7.98815, 1.9611616, -22.031042, 7.3638086, 2.3712668, -19.944632, 7.98815, 1.5335416, -22.031042, 7.988151, 7.7761884, 7.8527765, 15.352116, 9.199167, 8.88039, 16.222307, 9.098564, 7.4648857, 16.002731, 9.098564, 7.4648857, 16.002731, 9.199167, 8.88039, 16.222307, 10.9235325, 8.916273, 15.3749695, 9.098564, 7.4648857, 16.002731, 10.9235325, 8.916273, 15.3749695, 10.61574, 7.8335533, 15.204948, 7.7761884, 7.8527765, 15.352116, 7.562378, 8.858175, 15.848726, 9.199167, 8.88039, 16.222307, 7.2445464, 7.562926, 13.58247, 7.562378, 8.858175, 15.848726, 7.7761884, 7.8527765, 15.352116, 9.199167, 8.88039, 16.222307, 10.425425, 9.955207, 14.89032, 10.9235325, 8.916273, 15.3749695, 7.2445464, 7.562926, 13.58247, 7.1392436, 8.958778, 13.709131, 7.562378, 8.858175, 15.848726, 8.052368, 7.9555154, 11.936708, 7.1392436, 8.958778, 13.709131, 7.2445464, 7.562926, 13.58247, 7.562378, 8.858175, 15.848726, 9.38756, 10.309776, 15.220966, 9.199167, 8.88039, 16.222307, 9.199167, 8.88039, 16.222307, 9.38756, 10.309776, 15.220966, 10.425425, 9.955207, 14.89032, 7.1392436, 8.958778, 13.709131, 8.290742, 9.929361, 15.100925, 7.562378, 8.858175, 15.848726, 7.562378, 8.858175, 15.848726, 8.290742, 9.929361, 15.100925, 9.38756, 10.309776, 15.220966, 8.052368, 7.9555154, 11.936708, 7.8336453, 9.033964, 11.605206, 7.1392436, 8.958778, 13.709131, 7.8336453, 9.033964, 11.605206, 8.052368, 7.9555154, 11.936708, 9.536436, 7.534303, 11.340561, 7.1392436, 8.958778, 13.709131, 8.151478, 10.329214, 13.871677, 8.290742, 9.929361, 15.100925, 7.8336453, 9.033964, 11.605206, 8.151478, 10.329214, 13.871677, 7.1392436, 8.958778, 13.709131, 8.290742, 9.929361, 15.100925, 9.450357, 10.786097, 13.925932, 9.38756, 10.309776, 15.220966, 8.290742, 9.929361, 15.100925, 8.151478, 10.329214, 13.871677, 9.450357, 10.786097, 13.925932, 7.8336453, 9.033964, 11.605206, 8.411211, 10.007323, 12.5740795, 8.151478, 10.329214, 13.871677, 7.8336453, 9.033964, 11.605206, 9.536436, 7.534303, 11.340561, 9.549679, 8.935923, 11.339066, 8.411211, 10.007323, 12.5740795, 7.8336453, 9.033964, 11.605206, 9.549679, 8.935923, 11.339066, 9.549679, 8.935923, 11.339066, 9.536436, 7.534303, 11.340561, 10.884657, 7.817319, 12.306017, 8.151478, 10.329214, 13.871677, 8.411211, 10.007323, 12.5740795, 9.475134, 10.32366, 12.55806, 8.411211, 10.007323, 12.5740795, 9.549679, 8.935923, 11.339066, 9.475134, 10.32366, 12.55806, 8.151478, 10.329214, 13.871677, 9.475134, 10.32366, 12.55806, 9.450357, 10.786097, 13.925932, 9.549679, 8.935923, 11.339066, 10.884657, 7.817319, 12.306017, 11.177925, 8.823998, 12.293627, 9.475134, 10.32366, 12.55806, 9.549679, 8.935923, 11.339066, 11.177925, 8.823998, 12.293627, 11.39024, 7.4364767, 13.761036, 11.177925, 8.823998, 12.293627, 10.884657, 7.817319, 12.306017, 9.450357, 10.786097, 13.925932, 9.475134, 10.32366, 12.55806, 10.538631, 9.914196, 12.87995, 9.475134, 10.32366, 12.55806, 11.177925, 8.823998, 12.293627, 10.538631, 9.914196, 12.87995, 11.39024, 7.4364767, 13.761036, 11.609604, 8.857534, 13.852028, 11.177925, 8.823998, 12.293627, 11.609604, 8.857534, 13.852028, 10.538631, 9.914196, 12.87995, 11.177925, 8.823998, 12.293627, 10.61574, 7.8335533, 15.204948, 11.609604, 8.857534, 13.852028, 11.39024, 7.4364767, 13.761036, 10.61574, 7.8335533, 15.204948, 10.9235325, 8.916273, 15.3749695, 11.609604, 8.857534, 13.852028, 11.609604, 8.857534, 13.852028, 10.711004, 10.304009, 13.907562, 10.538631, 9.914196, 12.87995, 9.450357, 10.786097, 13.925932, 10.538631, 9.914196, 12.87995, 10.711004, 10.304009, 13.907562, 10.9235325, 8.916273, 15.3749695, 10.711004, 10.304009, 13.907562, 11.609604, 8.857534, 13.852028, 9.38756, 10.309776, 15.220966, 9.450357, 10.786097, 13.925932, 10.711004, 10.304009, 13.907562, 10.9235325, 8.916273, 15.3749695, 10.425425, 9.955207, 14.89032, 10.711004, 10.304009, 13.907562, 9.38756, 10.309776, 15.220966, 10.711004, 10.304009, 13.907562, 10.425425, 9.955207, 14.89032, 8.052368, 7.9555154, 11.936708, 9.298491, 7.030216, 13.635441, 9.536436, 7.534303, 11.340561, 8.052368, 7.9555154, 11.936708, 7.2445464, 7.562926, 13.58247, 9.298491, 7.030216, 13.635441, 9.536436, 7.534303, 11.340561, 9.298491, 7.030216, 13.635441, 11.39024, 7.4364767, 13.761036, 9.536436, 7.534303, 11.340561, 11.39024, 7.4364767, 13.761036, 10.884657, 7.817319, 12.306017, 9.298491, 7.030216, 13.635441, 10.61574, 7.8335533, 15.204948, 11.39024, 7.4364767, 13.761036, 9.298491, 7.030216, 13.635441, 9.098564, 7.4648857, 16.002731, 10.61574, 7.8335533, 15.204948, 7.2445464, 7.562926, 13.58247, 9.098564, 7.4648857, 16.002731, 9.298491, 7.030216, 13.635441, 7.2445464, 7.562926, 13.58247, 7.7761884, 7.8527765, 15.352116, 9.098564, 7.4648857, 16.002731, 5.392665, 7.074431, 14.52272, 6.3828983, 7.807067, 15.249163, 6.23808, 6.805727, 14.9206505, 6.23808, 6.805727, 14.9206505, 6.3828983, 7.807067, 15.249163, 7.6559334, 7.8126206, 14.855719, 6.23808, 6.805727, 14.9206505, 7.6559334, 7.8126206, 14.855719, 7.276586, 7.074431, 14.52272, 5.392665, 7.074431, 14.52272, 5.1824865, 7.8126206, 14.855719, 6.3828983, 7.807067, 15.249163, 5.1824865, 6.83328, 13.486778, 5.1824865, 7.8126206, 14.855719, 5.392665, 7.074431, 14.52272, 6.3828983, 7.807067, 15.249163, 7.3468595, 8.559354, 14.576548, 7.6559334, 7.8126206, 14.855719, 5.1824865, 6.83328, 13.486778, 5.111145, 7.829067, 13.674955, 5.1824865, 7.8126206, 14.855719, 5.772867, 7.108606, 12.548662, 5.111145, 7.829067, 13.674955, 5.1824865, 6.83328, 13.486778, 5.1824865, 7.8126206, 14.855719, 6.600126, 8.819301, 14.790783, 6.3828983, 7.807067, 15.249163, 6.3828983, 7.807067, 15.249163, 6.600126, 8.819301, 14.790783, 7.3468595, 8.559354, 14.576548, 5.111145, 7.829067, 13.674955, 5.772867, 8.559354, 14.576548, 5.1824865, 7.8126206, 14.855719, 5.1824865, 7.8126206, 14.855719, 5.772867, 8.559354, 14.576548, 6.600126, 8.819301, 14.790783, 5.772867, 7.108606, 12.548662, 5.7483034, 7.8455133, 12.49441, 5.111145, 7.829067, 13.674955, 5.7483034, 7.8455133, 12.49441, 5.772867, 7.108606, 12.548662, 6.600126, 6.8608336, 12.05312, 5.111145, 7.829067, 13.674955, 5.7483034, 8.824854, 13.863134, 5.772867, 8.559354, 14.576548, 5.7483034, 7.8455133, 12.49441, 5.7483034, 8.824854, 13.863134, 5.111145, 7.829067, 13.674955, 5.772867, 8.559354, 14.576548, 6.6725354, 9.156782, 13.925932, 6.600126, 8.819301, 14.790783, 5.772867, 8.559354, 14.576548, 5.7483034, 8.824854, 13.863134, 6.6725354, 9.156782, 13.925932, 5.7483034, 7.8455133, 12.49441, 6.02427, 8.574091, 13.052322, 5.7483034, 8.824854, 13.863134, 5.7483034, 7.8455133, 12.49441, 6.600126, 6.8608336, 12.05312, 6.6725354, 7.8510666, 12.100964, 6.02427, 8.574091, 13.052322, 5.7483034, 7.8455133, 12.49441, 6.6725354, 7.8510666, 12.100964, 6.6725354, 7.8510666, 12.100964, 6.600126, 6.8608336, 12.05312, 7.3468595, 7.108606, 12.548662, 5.7483034, 8.824854, 13.863134, 6.02427, 8.574091, 13.052322, 6.6725354, 8.830407, 12.935699, 6.02427, 8.574091, 13.052322, 6.6725354, 7.8510666, 12.100964, 6.6725354, 8.830407, 12.935699, 5.7483034, 8.824854, 13.863134, 6.6725354, 8.830407, 12.935699, 6.6725354, 9.156782, 13.925932, 6.6725354, 7.8510666, 12.100964, 7.3468595, 7.108606, 12.548662, 7.5245714, 7.8455133, 12.49441, 6.6725354, 8.830407, 12.935699, 6.6725354, 7.8510666, 12.100964, 7.5245714, 7.8455133, 12.49441, 7.6559334, 6.83328, 13.486778, 7.5245714, 7.8455133, 12.49441, 7.3468595, 7.108606, 12.548662, 6.6725354, 9.156782, 13.925932, 6.6725354, 8.830407, 12.935699, 7.2885475, 8.574091, 13.052322, 6.6725354, 8.830407, 12.935699, 7.5245714, 7.8455133, 12.49441, 7.2885475, 8.574091, 13.052322, 7.6559334, 6.83328, 13.486778, 7.9442887, 7.829067, 13.674955, 7.5245714, 7.8455133, 12.49441, 7.9442887, 7.829067, 13.674955, 7.2885475, 8.574091, 13.052322, 7.5245714, 7.8455133, 12.49441, 7.276586, 7.074431, 14.52272, 7.9442887, 7.829067, 13.674955, 7.6559334, 6.83328, 13.486778, 7.276586, 7.074431, 14.52272, 7.6559334, 7.8126206, 14.855719, 7.9442887, 7.829067, 13.674955, 7.9442887, 7.829067, 13.674955, 7.5245714, 8.824854, 13.863134, 7.2885475, 8.574091, 13.052322, 6.6725354, 9.156782, 13.925932, 7.2885475, 8.574091, 13.052322, 7.5245714, 8.824854, 13.863134, 7.6559334, 7.8126206, 14.855719, 7.5245714, 8.824854, 13.863134, 7.9442887, 7.829067, 13.674955, 6.600126, 8.819301, 14.790783, 6.6725354, 9.156782, 13.925932, 7.5245714, 8.824854, 13.863134, 7.6559334, 7.8126206, 14.855719, 7.3468595, 8.559354, 14.576548, 7.5245714, 8.824854, 13.863134, 6.600126, 8.819301, 14.790783, 7.5245714, 8.824854, 13.863134, 7.3468595, 8.559354, 14.576548, 5.772867, 7.108606, 12.548662, 6.3828983, 6.5013514, 13.424194, 6.600126, 6.8608336, 12.05312, 5.772867, 7.108606, 12.548662, 5.1824865, 6.83328, 13.486778, 6.3828983, 6.5013514, 13.424194, 6.600126, 6.8608336, 12.05312, 6.3828983, 6.5013514, 13.424194, 7.6559334, 6.83328, 13.486778, 6.600126, 6.8608336, 12.05312, 7.6559334, 6.83328, 13.486778, 7.3468595, 7.108606, 12.548662, 6.3828983, 6.5013514, 13.424194, 7.276586, 7.074431, 14.52272, 7.6559334, 6.83328, 13.486778, 6.3828983, 6.5013514, 13.424194, 6.23808, 6.805727, 14.9206505, 7.276586, 7.074431, 14.52272, 5.1824865, 6.83328, 13.486778, 6.23808, 6.805727, 14.9206505, 6.3828983, 6.5013514, 13.424194, 5.1824865, 6.83328, 13.486778, 5.392665, 7.074431, 14.52272, 6.23808, 6.805727, 14.9206505, 9.232489, 5.4846325, 13.925934, 8.13439, 5.7744827, 13.729637, 9.182293, 5.342804, 13.718958, 9.232489, 5.4846325, 13.925934, 8.220682, 5.8879023, 13.925932, 8.13439, 5.7744827, 13.729637, 8.220682, 5.8879023, 13.925932, 7.0958834, 6.874291, 13.868902, 8.13439, 5.7744827, 13.729637, 8.220682, 5.8879023, 13.925932, 7.137321, 6.8738637, 13.925932, 7.0958834, 6.874291, 13.868902, 8.13439, 5.7744827, 13.729637, 7.0958834, 6.874291, 13.868902, 7.0288143, 6.874932, 13.89069, 9.182293, 5.342804, 13.718958, 8.13439, 5.7744827, 13.729637, 7.994697, 5.590576, 13.804609, 8.13439, 5.7744827, 13.729637, 7.0288143, 6.874932, 13.89069, 7.994697, 5.590576, 13.804609, 9.182293, 5.342804, 13.718958, 7.994697, 5.590576, 13.804609, 9.101341, 5.1134014, 13.797989, 7.994697, 5.590576, 13.804609, 7.0288143, 6.874932, 13.89069, 7.0288143, 6.874932, 13.961176, 9.101341, 5.1134014, 13.797989, 7.994697, 5.590576, 13.804609, 7.994697, 5.590576, 14.047256, 7.994697, 5.590576, 13.804609, 7.0288143, 6.874932, 13.961176, 7.994697, 5.590576, 14.047256, 9.101341, 5.1134014, 13.797989, 7.994697, 5.590576, 14.047256, 9.101341, 5.113402, 14.053877, 7.994697, 5.590576, 14.047256, 7.0288143, 6.874932, 13.961176, 7.0958834, 6.874291, 13.982964, 9.101341, 5.113402, 14.053877, 7.994697, 5.590576, 14.047256, 8.13439, 5.7744827, 14.122229, 7.994697, 5.590576, 14.047256, 7.0958834, 6.874291, 13.982964, 8.13439, 5.7744827, 14.122229, 8.13439, 5.7744827, 14.122229, 7.0958834, 6.874291, 13.982964, 7.137321, 6.8738637, 13.925932, 9.101341, 5.113402, 14.053877, 8.13439, 5.7744827, 14.122229, 9.182293, 5.3428044, 14.132908, 8.13439, 5.7744827, 14.122229, 7.137321, 6.8738637, 13.925932, 8.220682, 5.8879023, 13.925932, 9.182293, 5.3428044, 14.132908, 8.13439, 5.7744827, 14.122229, 8.220682, 5.8879023, 13.925932, 9.182293, 5.3428044, 14.132908, 8.220682, 5.8879023, 13.925932, 9.232489, 5.4846325, 13.925934, 8.1010685, 3.1833441, 13.925934, 9.1000595, 5.240492, 14.151705, 8.528688, 3.1833444, 14.550276, 8.1010685, 3.1833441, 13.925934, 8.938793, 5.2697544, 13.925934, 9.1000595, 5.240492, 14.151705, 8.938793, 5.2697544, 13.925934, 9.181226, 7.30661, 14.025682, 9.1000595, 5.240492, 14.151705, 8.938793, 5.2697544, 13.925934, 9.109243, 7.314086, 13.925932, 9.181226, 7.30661, 14.025682, 9.1000595, 5.240492, 14.151705, 9.181226, 7.30661, 14.025682, 9.298063, 7.2942214, 13.987661, 8.528688, 3.1833444, 14.550276, 9.1000595, 5.240492, 14.151705, 9.361288, 5.1928596, 14.0654125, 9.1000595, 5.240492, 14.151705, 9.298063, 7.2942214, 13.987661, 9.361288, 5.1928596, 14.0654125, 8.528688, 3.1833444, 14.550276, 9.361288, 5.1928596, 14.0654125, 9.220314, 3.1833444, 14.311903, 9.361288, 5.1928596, 14.0654125, 9.298063, 7.2942214, 13.987661, 9.298063, 7.2942214, 13.8642025, 9.220314, 3.1833444, 14.311903, 9.361288, 5.1928596, 14.0654125, 9.361074, 5.1928596, 13.786455, 9.361288, 5.1928596, 14.0654125, 9.298063, 7.2942214, 13.8642025, 9.361074, 5.1928596, 13.786455, 9.220314, 3.1833444, 14.311903, 9.361074, 5.1928596, 13.786455, 9.220314, 3.1833441, 13.539965, 9.361074, 5.1928596, 13.786455, 9.298063, 7.2942214, 13.8642025, 9.181226, 7.30661, 13.826183, 9.220314, 3.1833441, 13.539965, 9.361074, 5.1928596, 13.786455, 9.1000595, 5.240492, 13.700161, 9.361074, 5.1928596, 13.786455, 9.181226, 7.30661, 13.826183, 9.1000595, 5.240492, 13.700161, 9.1000595, 5.240492, 13.700161, 9.181226, 7.30661, 13.826183, 9.109243, 7.314086, 13.925932, 9.220314, 3.1833441, 13.539965, 9.1000595, 5.240492, 13.700161, 8.528688, 3.183344, 13.301592, 9.1000595, 5.240492, 13.700161, 9.109243, 7.314086, 13.925932, 8.938793, 5.2697544, 13.925934, 8.528688, 3.183344, 13.301592, 9.1000595, 5.240492, 13.700161, 8.938793, 5.2697544, 13.925934, 8.528688, 3.183344, 13.301592, 8.938793, 5.2697544, 13.925934, 8.1010685, 3.1833441, 13.925934, -21.010672, 7.8103647, 2.1272998, -19.587692, 8.837978, 2.9974916, -19.688295, 7.422474, 2.7779148, -19.688295, 7.422474, 2.7779148, -19.587692, 8.837978, 2.9974916, -17.863327, 8.873861, 2.1501544, -19.688295, 7.422474, 2.7779148, -17.863327, 8.873861, 2.1501544, -18.17112, 7.7911415, 1.9801317, -21.010672, 7.8103647, 2.1272998, -21.224482, 8.815763, 2.6239111, -19.587692, 8.837978, 2.9974916, -21.542313, 7.520514, 0.35765326, -21.224482, 8.815763, 2.6239111, -21.010672, 7.8103647, 2.1272998, -19.587692, 8.837978, 2.9974916, -18.361435, 9.912795, 1.6655035, -17.863327, 8.873861, 2.1501544, -21.542313, 7.520514, 0.35765326, -21.647615, 8.916367, 0.4843155, -21.224482, 8.815763, 2.6239111, -20.734491, 7.9131036, -1.2881074, -21.647615, 8.916367, 0.4843155, -21.542313, 7.520514, 0.35765326, -21.224482, 8.815763, 2.6239111, -19.3993, 10.2673645, 1.9961507, -19.587692, 8.837978, 2.9974916, -19.587692, 8.837978, 2.9974916, -19.3993, 10.2673645, 1.9961507, -18.361435, 9.912795, 1.6655035, -21.647615, 8.916367, 0.4843155, -20.496117, 9.88695, 1.8761096, -21.224482, 8.815763, 2.6239111, -21.224482, 8.815763, 2.6239111, -20.496117, 9.88695, 1.8761096, -19.3993, 10.2673645, 1.9961507, -20.734491, 7.9131036, -1.2881074, -20.953215, 8.991552, -1.6196096, -21.647615, 8.916367, 0.4843155, -20.953215, 8.991552, -1.6196096, -20.734491, 7.9131036, -1.2881074, -19.250423, 7.4918914, -1.8842552, -21.647615, 8.916367, 0.4843155, -20.635382, 10.286802, 0.6468619, -20.496117, 9.88695, 1.8761096, -20.953215, 8.991552, -1.6196096, -20.635382, 10.286802, 0.6468619, -21.647615, 8.916367, 0.4843155, -20.496117, 9.88695, 1.8761096, -19.336502, 10.743685, 0.70111525, -19.3993, 10.2673645, 1.9961507, -20.496117, 9.88695, 1.8761096, -20.635382, 10.286802, 0.6468619, -19.336502, 10.743685, 0.70111525, -20.953215, 8.991552, -1.6196096, -20.375648, 9.964911, -0.65073633, -20.635382, 10.286802, 0.6468619, -20.953215, 8.991552, -1.6196096, -19.250423, 7.4918914, -1.8842552, -19.23718, 8.893511, -1.885751, -20.375648, 9.964911, -0.65073633, -20.953215, 8.991552, -1.6196096, -19.23718, 8.893511, -1.885751, -19.23718, 8.893511, -1.885751, -19.250423, 7.4918914, -1.8842552, -17.902203, 7.774907, -0.91879916, -20.635382, 10.286802, 0.6468619, -20.375648, 9.964911, -0.65073633, -19.311726, 10.281248, -0.66675615, -20.375648, 9.964911, -0.65073633, -19.23718, 8.893511, -1.885751, -19.311726, 10.281248, -0.66675615, -20.635382, 10.286802, 0.6468619, -19.311726, 10.281248, -0.66675615, -19.336502, 10.743685, 0.70111525, -19.23718, 8.893511, -1.885751, -17.902203, 7.774907, -0.91879916, -17.608934, 8.781587, -0.9311881, -19.311726, 10.281248, -0.66675615, -19.23718, 8.893511, -1.885751, -17.608934, 8.781587, -0.9311881, -17.39662, 7.394065, 0.53621995, -17.608934, 8.781587, -0.9311881, -17.902203, 7.774907, -0.91879916, -19.336502, 10.743685, 0.70111525, -19.311726, 10.281248, -0.66675615, -18.248228, 9.871784, -0.34486628, -19.311726, 10.281248, -0.66675615, -17.608934, 8.781587, -0.9311881, -18.248228, 9.871784, -0.34486628, -17.39662, 7.394065, 0.53621995, -17.177256, 8.815123, 0.6272116, -17.608934, 8.781587, -0.9311881, -17.177256, 8.815123, 0.6272116, -18.248228, 9.871784, -0.34486628, -17.608934, 8.781587, -0.9311881, -18.17112, 7.7911415, 1.9801317, -17.177256, 8.815123, 0.6272116, -17.39662, 7.394065, 0.53621995, -18.17112, 7.7911415, 1.9801317, -17.863327, 8.873861, 2.1501544, -17.177256, 8.815123, 0.6272116, -17.177256, 8.815123, 0.6272116, -18.075855, 10.261598, 0.6827462, -18.248228, 9.871784, -0.34486628, -19.336502, 10.743685, 0.70111525, -18.248228, 9.871784, -0.34486628, -18.075855, 10.261598, 0.6827462, -17.863327, 8.873861, 2.1501544, -18.075855, 10.261598, 0.6827462, -17.177256, 8.815123, 0.6272116, -19.3993, 10.2673645, 1.9961507, -19.336502, 10.743685, 0.70111525, -18.075855, 10.261598, 0.6827462, -17.863327, 8.873861, 2.1501544, -18.361435, 9.912795, 1.6655035, -18.075855, 10.261598, 0.6827462, -19.3993, 10.2673645, 1.9961507, -18.075855, 10.261598, 0.6827462, -18.361435, 9.912795, 1.6655035, -20.734491, 7.9131036, -1.2881074, -19.488369, 6.9878044, 0.41062546, -19.250423, 7.4918914, -1.8842552, -20.734491, 7.9131036, -1.2881074, -21.542313, 7.520514, 0.35765326, -19.488369, 6.9878044, 0.41062546, -19.250423, 7.4918914, -1.8842552, -19.488369, 6.9878044, 0.41062546, -17.39662, 7.394065, 0.53621995, -19.250423, 7.4918914, -1.8842552, -17.39662, 7.394065, 0.53621995, -17.902203, 7.774907, -0.91879916, -19.488369, 6.9878044, 0.41062546, -18.17112, 7.7911415, 1.9801317, -17.39662, 7.394065, 0.53621995, -19.488369, 6.9878044, 0.41062546, -19.688295, 7.422474, 2.7779148, -18.17112, 7.7911415, 1.9801317, -21.542313, 7.520514, 0.35765326, -19.688295, 7.422474, 2.7779148, -19.488369, 6.9878044, 0.41062546, -21.542313, 7.520514, 0.35765326, -21.010672, 7.8103647, 2.1272998, -19.688295, 7.422474, 2.7779148, -23.394194, 7.032019, 1.297905, -22.403961, 7.764655, 2.0243464, -22.54878, 6.763315, 1.6958355, -22.54878, 6.763315, 1.6958355, -22.403961, 7.764655, 2.0243464, -21.130926, 7.770209, 1.6309018, -22.54878, 6.763315, 1.6958355, -21.130926, 7.770209, 1.6309018, -21.510273, 7.032019, 1.297905, -23.394194, 7.032019, 1.297905, -23.604374, 7.770209, 1.6309018, -22.403961, 7.764655, 2.0243464, -23.604374, 6.7908683, 0.2619624, -23.604374, 7.770209, 1.6309018, -23.394194, 7.032019, 1.297905, -22.403961, 7.764655, 2.0243464, -21.44, 8.516942, 1.3517308, -21.130926, 7.770209, 1.6309018, -23.604374, 6.7908683, 0.2619624, -23.675714, 7.7866554, 0.45014048, -23.604374, 7.770209, 1.6309018, -23.013992, 7.066194, -0.6761534, -23.675714, 7.7866554, 0.45014048, -23.604374, 6.7908683, 0.2619624, -23.604374, 7.770209, 1.6309018, -22.186733, 8.776889, 1.5659679, -22.403961, 7.764655, 2.0243464, -22.403961, 7.764655, 2.0243464, -22.186733, 8.776889, 1.5659679, -21.44, 8.516942, 1.3517308, -23.675714, 7.7866554, 0.45014048, -23.013992, 8.516942, 1.3517308, -23.604374, 7.770209, 1.6309018, -23.604374, 7.770209, 1.6309018, -23.013992, 8.516942, 1.3517308, -22.186733, 8.776889, 1.5659679, -23.013992, 7.066194, -0.6761534, -23.038557, 7.8031015, -0.73040724, -23.675714, 7.7866554, 0.45014048, -23.038557, 7.8031015, -0.73040724, -23.013992, 7.066194, -0.6761534, -22.186733, 6.818422, -1.1716971, -23.675714, 7.7866554, 0.45014048, -23.038557, 8.782442, 0.63831854, -23.013992, 8.516942, 1.3517308, -23.038557, 7.8031015, -0.73040724, -23.038557, 8.782442, 0.63831854, -23.675714, 7.7866554, 0.45014048, -23.013992, 8.516942, 1.3517308, -22.114325, 9.11437, 0.70111585, -22.186733, 8.776889, 1.5659679, -23.013992, 8.516942, 1.3517308, -23.038557, 8.782442, 0.63831854, -22.114325, 9.11437, 0.70111585, -23.038557, 7.8031015, -0.73040724, -22.76259, 8.531679, -0.17249346, -23.038557, 8.782442, 0.63831854, -23.038557, 7.8031015, -0.73040724, -22.186733, 6.818422, -1.1716971, -22.114325, 7.808655, -1.1238518, -22.76259, 8.531679, -0.17249346, -23.038557, 7.8031015, -0.73040724, -22.114325, 7.808655, -1.1238518, -22.114325, 7.808655, -1.1238518, -22.186733, 6.818422, -1.1716971, -21.44, 7.066194, -0.6761534, -23.038557, 8.782442, 0.63831854, -22.76259, 8.531679, -0.17249346, -22.114325, 8.787995, -0.28911734, -22.76259, 8.531679, -0.17249346, -22.114325, 7.808655, -1.1238518, -22.114325, 8.787995, -0.28911734, -23.038557, 8.782442, 0.63831854, -22.114325, 8.787995, -0.28911734, -22.114325, 9.11437, 0.70111585, -22.114325, 7.808655, -1.1238518, -21.44, 7.066194, -0.6761534, -21.262287, 7.8031015, -0.73040724, -22.114325, 8.787995, -0.28911734, -22.114325, 7.808655, -1.1238518, -21.262287, 7.8031015, -0.73040724, -21.130926, 6.7908683, 0.2619624, -21.262287, 7.8031015, -0.73040724, -21.44, 7.066194, -0.6761534, -22.114325, 9.11437, 0.70111585, -22.114325, 8.787995, -0.28911734, -21.498312, 8.531679, -0.17249346, -22.114325, 8.787995, -0.28911734, -21.262287, 7.8031015, -0.73040724, -21.498312, 8.531679, -0.17249346, -21.130926, 6.7908683, 0.2619624, -20.842571, 7.7866554, 0.45014048, -21.262287, 7.8031015, -0.73040724, -20.842571, 7.7866554, 0.45014048, -21.498312, 8.531679, -0.17249346, -21.262287, 7.8031015, -0.73040724, -21.510273, 7.032019, 1.297905, -20.842571, 7.7866554, 0.45014048, -21.130926, 6.7908683, 0.2619624, -21.510273, 7.032019, 1.297905, -21.130926, 7.770209, 1.6309018, -20.842571, 7.7866554, 0.45014048, -20.842571, 7.7866554, 0.45014048, -21.262287, 8.782442, 0.63831854, -21.498312, 8.531679, -0.17249346, -22.114325, 9.11437, 0.70111585, -21.498312, 8.531679, -0.17249346, -21.262287, 8.782442, 0.63831854, -21.130926, 7.770209, 1.6309018, -21.262287, 8.782442, 0.63831854, -20.842571, 7.7866554, 0.45014048, -22.186733, 8.776889, 1.5659679, -22.114325, 9.11437, 0.70111585, -21.262287, 8.782442, 0.63831854, -21.130926, 7.770209, 1.6309018, -21.44, 8.516942, 1.3517308, -21.262287, 8.782442, 0.63831854, -22.186733, 8.776889, 1.5659679, -21.262287, 8.782442, 0.63831854, -21.44, 8.516942, 1.3517308, -23.013992, 7.066194, -0.6761534, -22.403961, 6.4589396, 0.19937873, -22.186733, 6.818422, -1.1716971, -23.013992, 7.066194, -0.6761534, -23.604374, 6.7908683, 0.2619624, -22.403961, 6.4589396, 0.19937873, -22.186733, 6.818422, -1.1716971, -22.403961, 6.4589396, 0.19937873, -21.130926, 6.7908683, 0.2619624, -22.186733, 6.818422, -1.1716971, -21.130926, 6.7908683, 0.2619624, -21.44, 7.066194, -0.6761534, -22.403961, 6.4589396, 0.19937873, -21.510273, 7.032019, 1.297905, -21.130926, 6.7908683, 0.2619624, -22.403961, 6.4589396, 0.19937873, -22.54878, 6.763315, 1.6958355, -21.510273, 7.032019, 1.297905, -23.604374, 6.7908683, 0.2619624, -22.54878, 6.763315, 1.6958355, -22.403961, 6.4589396, 0.19937873, -23.604374, 6.7908683, 0.2619624, -23.394194, 7.032019, 1.297905, -22.54878, 6.763315, 1.6958355, -19.55437, 5.4422207, 0.70111716, -20.65247, 5.732071, 0.5048219, -19.604567, 5.300392, 0.49414217, -19.55437, 5.4422207, 0.70111716, -20.566177, 5.8454905, 0.70111704, -20.65247, 5.732071, 0.5048219, -20.566177, 5.8454905, 0.70111704, -21.690977, 6.831879, 0.64408636, -20.65247, 5.732071, 0.5048219, -20.566177, 5.8454905, 0.70111704, -21.649538, 6.831452, 0.7011167, -21.690977, 6.831879, 0.64408636, -20.65247, 5.732071, 0.5048219, -21.690977, 6.831879, 0.64408636, -21.758045, 6.83252, 0.66587317, -19.604567, 5.300392, 0.49414217, -20.65247, 5.732071, 0.5048219, -20.792162, 5.5481644, 0.5797943, -20.65247, 5.732071, 0.5048219, -21.758045, 6.83252, 0.66587317, -20.792162, 5.5481644, 0.5797943, -19.604567, 5.300392, 0.49414217, -20.792162, 5.5481644, 0.5797943, -19.685518, 5.0709896, 0.57317305, -20.792162, 5.5481644, 0.5797943, -21.758045, 6.83252, 0.66587317, -21.758045, 6.83252, 0.7363601, -19.685518, 5.0709896, 0.57317305, -20.792162, 5.5481644, 0.5797943, -20.792162, 5.5481644, 0.8224399, -20.792162, 5.5481644, 0.5797943, -21.758045, 6.83252, 0.7363601, -20.792162, 5.5481644, 0.8224399, -19.685518, 5.0709896, 0.57317305, -20.792162, 5.5481644, 0.8224399, -19.685518, 5.07099, 0.8290615, -20.792162, 5.5481644, 0.8224399, -21.758045, 6.83252, 0.7363601, -21.690977, 6.831879, 0.7581469, -19.685518, 5.07099, 0.8290615, -20.792162, 5.5481644, 0.8224399, -20.65247, 5.732071, 0.8974122, -20.792162, 5.5481644, 0.8224399, -21.690977, 6.831879, 0.7581469, -20.65247, 5.732071, 0.8974122, -20.65247, 5.732071, 0.8974122, -21.690977, 6.831879, 0.7581469, -21.649538, 6.831452, 0.7011167, -19.685518, 5.07099, 0.8290615, -20.65247, 5.732071, 0.8974122, -19.604567, 5.3003926, 0.90809214, -20.65247, 5.732071, 0.8974122, -21.649538, 6.831452, 0.7011167, -20.566177, 5.8454905, 0.70111704, -19.604567, 5.3003926, 0.90809214, -20.65247, 5.732071, 0.8974122, -20.566177, 5.8454905, 0.70111704, -19.604567, 5.3003926, 0.90809214, -20.566177, 5.8454905, 0.70111704, -19.55437, 5.4422207, 0.70111716, -20.685791, 3.1409323, 0.701118, -19.6868, 5.19808, 0.9268887, -20.258171, 3.1409326, 1.3254604, -20.685791, 3.1409323, 0.701118, -19.848066, 5.2273426, 0.70111716, -19.6868, 5.19808, 0.9268887, -19.848066, 5.2273426, 0.70111716, -19.605635, 7.2641983, 0.800866, -19.6868, 5.19808, 0.9268887, -19.848066, 5.2273426, 0.70111716, -19.677616, 7.271674, 0.70111644, -19.605635, 7.2641983, 0.800866, -19.6868, 5.19808, 0.9268887, -19.605635, 7.2641983, 0.800866, -19.488796, 7.2518096, 0.7628459, -20.258171, 3.1409326, 1.3254604, -19.6868, 5.19808, 0.9268887, -19.425571, 5.150448, 0.8405957, -19.6868, 5.19808, 0.9268887, -19.488796, 7.2518096, 0.7628459, -19.425571, 5.150448, 0.8405957, -20.258171, 3.1409326, 1.3254604, -19.425571, 5.150448, 0.8405957, -19.566545, 3.1409326, 1.0870868, -19.425571, 5.150448, 0.8405957, -19.488796, 7.2518096, 0.7628459, -19.488796, 7.2518096, 0.63938713, -19.566545, 3.1409326, 1.0870868, -19.425571, 5.150448, 0.8405957, -19.425785, 5.150448, 0.5616387, -19.425571, 5.150448, 0.8405957, -19.488796, 7.2518096, 0.63938713, -19.425785, 5.150448, 0.5616387, -19.566545, 3.1409326, 1.0870868, -19.425785, 5.150448, 0.5616387, -19.566545, 3.1409323, 0.3151492, -19.425785, 5.150448, 0.5616387, -19.488796, 7.2518096, 0.63938713, -19.605635, 7.2641983, 0.601367, -19.566545, 3.1409323, 0.3151492, -19.425785, 5.150448, 0.5616387, -19.6868, 5.19808, 0.47534573, -19.425785, 5.150448, 0.5616387, -19.605635, 7.2641983, 0.601367, -19.6868, 5.19808, 0.47534573, -19.6868, 5.19808, 0.47534573, -19.605635, 7.2641983, 0.601367, -19.677616, 7.271674, 0.70111644, -19.566545, 3.1409323, 0.3151492, -19.6868, 5.19808, 0.47534573, -20.258171, 3.140932, 0.07677555, -19.6868, 5.19808, 0.47534573, -19.677616, 7.271674, 0.70111644, -19.848066, 5.2273426, 0.70111716, -20.258171, 3.140932, 0.07677555, -19.6868, 5.19808, 0.47534573, -19.848066, 5.2273426, 0.70111716, -20.258171, 3.140932, 0.07677555, -19.848066, 5.2273426, 0.70111716, -20.685791, 3.1409323, 0.701118, -9.178474, 7.896264, -9.667162, -7.7554946, 8.923878, -8.79697, -7.8560977, 7.5083733, -9.016546, -7.8560977, 7.5083733, -9.016546, -7.7554946, 8.923878, -8.79697, -6.0311303, 8.959761, -9.644308, -7.8560977, 7.5083733, -9.016546, -6.0311303, 8.959761, -9.644308, -6.338922, 7.877041, -9.814329, -9.178474, 7.896264, -9.667162, -9.392284, 8.901663, -9.170551, -7.7554946, 8.923878, -8.79697, -9.710115, 7.6064134, -11.436808, -9.392284, 8.901663, -9.170551, -9.178474, 7.896264, -9.667162, -7.7554946, 8.923878, -8.79697, -6.5292373, 9.998694, -10.128958, -6.0311303, 8.959761, -9.644308, -9.710115, 7.6064134, -11.436808, -9.815418, 9.002266, -11.310146, -9.392284, 8.901663, -9.170551, -8.902294, 7.999003, -13.082569, -9.815418, 9.002266, -11.310146, -9.710115, 7.6064134, -11.436808, -9.392284, 8.901663, -9.170551, -7.567102, 10.353264, -9.798311, -7.7554946, 8.923878, -8.79697, -7.7554946, 8.923878, -8.79697, -7.567102, 10.353264, -9.798311, -6.5292373, 9.998694, -10.128958, -9.815418, 9.002266, -11.310146, -8.663919, 9.972849, -9.918352, -9.392284, 8.901663, -9.170551, -9.392284, 8.901663, -9.170551, -8.663919, 9.972849, -9.918352, -7.567102, 10.353264, -9.798311, -8.902294, 7.999003, -13.082569, -9.121016, 9.077452, -13.414072, -9.815418, 9.002266, -11.310146, -9.121016, 9.077452, -13.414072, -8.902294, 7.999003, -13.082569, -7.418226, 7.5777907, -13.678717, -9.815418, 9.002266, -11.310146, -8.8031845, 10.372702, -11.1476, -8.663919, 9.972849, -9.918352, -9.121016, 9.077452, -13.414072, -8.8031845, 10.372702, -11.1476, -9.815418, 9.002266, -11.310146, -8.663919, 9.972849, -9.918352, -7.5043044, 10.829584, -11.093346, -7.567102, 10.353264, -9.798311, -8.663919, 9.972849, -9.918352, -8.8031845, 10.372702, -11.1476, -7.5043044, 10.829584, -11.093346, -9.121016, 9.077452, -13.414072, -8.543451, 10.050811, -12.445198, -8.8031845, 10.372702, -11.1476, -9.121016, 9.077452, -13.414072, -7.418226, 7.5777907, -13.678717, -7.404982, 8.97941, -13.680212, -8.543451, 10.050811, -12.445198, -9.121016, 9.077452, -13.414072, -7.404982, 8.97941, -13.680212, -7.404982, 8.97941, -13.680212, -7.418226, 7.5777907, -13.678717, -6.070005, 7.8608065, -12.713261, -8.8031845, 10.372702, -11.1476, -8.543451, 10.050811, -12.445198, -7.479528, 10.367147, -12.461218, -8.543451, 10.050811, -12.445198, -7.404982, 8.97941, -13.680212, -7.479528, 10.367147, -12.461218, -8.8031845, 10.372702, -11.1476, -7.479528, 10.367147, -12.461218, -7.5043044, 10.829584, -11.093346, -7.404982, 8.97941, -13.680212, -6.070005, 7.8608065, -12.713261, -5.7767367, 8.867486, -12.725651, -7.479528, 10.367147, -12.461218, -7.404982, 8.97941, -13.680212, -5.7767367, 8.867486, -12.725651, -5.564422, 7.4799643, -11.258242, -5.7767367, 8.867486, -12.725651, -6.070005, 7.8608065, -12.713261, -7.5043044, 10.829584, -11.093346, -7.479528, 10.367147, -12.461218, -6.4160304, 9.957684, -12.139328, -7.479528, 10.367147, -12.461218, -5.7767367, 8.867486, -12.725651, -6.4160304, 9.957684, -12.139328, -5.564422, 7.4799643, -11.258242, -5.345058, 8.901022, -11.16725, -5.7767367, 8.867486, -12.725651, -5.345058, 8.901022, -11.16725, -6.4160304, 9.957684, -12.139328, -5.7767367, 8.867486, -12.725651, -6.338922, 7.877041, -9.814329, -5.345058, 8.901022, -11.16725, -5.564422, 7.4799643, -11.258242, -6.338922, 7.877041, -9.814329, -6.0311303, 8.959761, -9.644308, -5.345058, 8.901022, -11.16725, -5.345058, 8.901022, -11.16725, -6.2436585, 10.347497, -11.111715, -6.4160304, 9.957684, -12.139328, -7.5043044, 10.829584, -11.093346, -6.4160304, 9.957684, -12.139328, -6.2436585, 10.347497, -11.111715, -6.0311303, 8.959761, -9.644308, -6.2436585, 10.347497, -11.111715, -5.345058, 8.901022, -11.16725, -7.567102, 10.353264, -9.798311, -7.5043044, 10.829584, -11.093346, -6.2436585, 10.347497, -11.111715, -6.0311303, 8.959761, -9.644308, -6.5292373, 9.998694, -10.128958, -6.2436585, 10.347497, -11.111715, -7.567102, 10.353264, -9.798311, -6.2436585, 10.347497, -11.111715, -6.5292373, 9.998694, -10.128958, -8.902294, 7.999003, -13.082569, -7.6561713, 7.073704, -11.383837, -7.418226, 7.5777907, -13.678717, -8.902294, 7.999003, -13.082569, -9.710115, 7.6064134, -11.436808, -7.6561713, 7.073704, -11.383837, -7.418226, 7.5777907, -13.678717, -7.6561713, 7.073704, -11.383837, -5.564422, 7.4799643, -11.258242, -7.418226, 7.5777907, -13.678717, -5.564422, 7.4799643, -11.258242, -6.070005, 7.8608065, -12.713261, -7.6561713, 7.073704, -11.383837, -6.338922, 7.877041, -9.814329, -5.564422, 7.4799643, -11.258242, -7.6561713, 7.073704, -11.383837, -7.8560977, 7.5083733, -9.016546, -6.338922, 7.877041, -9.814329, -9.710115, 7.6064134, -11.436808, -7.8560977, 7.5083733, -9.016546, -7.6561713, 7.073704, -11.383837, -9.710115, 7.6064134, -11.436808, -9.178474, 7.896264, -9.667162, -7.8560977, 7.5083733, -9.016546, -11.561996, 7.1179185, -10.496557, -10.571764, 7.8505545, -9.770115, -10.716581, 6.8492146, -10.098627, -10.716581, 6.8492146, -10.098627, -10.571764, 7.8505545, -9.770115, -9.298729, 7.856108, -10.163559, -10.716581, 6.8492146, -10.098627, -9.298729, 7.856108, -10.163559, -9.678076, 7.1179185, -10.496557, -11.561996, 7.1179185, -10.496557, -11.772175, 7.856108, -10.163559, -10.571764, 7.8505545, -9.770115, -11.772175, 6.8767676, -11.532499, -11.772175, 7.856108, -10.163559, -11.561996, 7.1179185, -10.496557, -10.571764, 7.8505545, -9.770115, -9.607803, 8.602841, -10.44273, -9.298729, 7.856108, -10.163559, -11.772175, 6.8767676, -11.532499, -11.843517, 7.872555, -11.344322, -11.772175, 7.856108, -10.163559, -11.181795, 7.1520934, -12.470615, -11.843517, 7.872555, -11.344322, -11.772175, 6.8767676, -11.532499, -11.772175, 7.856108, -10.163559, -10.354536, 8.862788, -10.228495, -10.571764, 7.8505545, -9.770115, -10.571764, 7.8505545, -9.770115, -10.354536, 8.862788, -10.228495, -9.607803, 8.602841, -10.44273, -11.843517, 7.872555, -11.344322, -11.181795, 8.602841, -10.44273, -11.772175, 7.856108, -10.163559, -11.772175, 7.856108, -10.163559, -11.181795, 8.602841, -10.44273, -10.354536, 8.862788, -10.228495, -11.181795, 7.1520934, -12.470615, -11.206358, 7.889001, -12.524868, -11.843517, 7.872555, -11.344322, -11.206358, 7.889001, -12.524868, -11.181795, 7.1520934, -12.470615, -10.354536, 6.904321, -12.966158, -11.843517, 7.872555, -11.344322, -11.206358, 8.868341, -11.156143, -11.181795, 8.602841, -10.44273, -11.206358, 7.889001, -12.524868, -11.206358, 8.868341, -11.156143, -11.843517, 7.872555, -11.344322, -11.181795, 8.602841, -10.44273, -10.282127, 9.20027, -11.093346, -10.354536, 8.862788, -10.228495, -11.181795, 8.602841, -10.44273, -11.206358, 8.868341, -11.156143, -10.282127, 9.20027, -11.093346, -11.206358, 7.889001, -12.524868, -10.930391, 8.6175785, -11.966955, -11.206358, 8.868341, -11.156143, -11.206358, 7.889001, -12.524868, -10.354536, 6.904321, -12.966158, -10.282127, 7.894554, -12.918314, -10.930391, 8.6175785, -11.966955, -11.206358, 7.889001, -12.524868, -10.282127, 7.894554, -12.918314, -10.282127, 7.894554, -12.918314, -10.354536, 6.904321, -12.966158, -9.607803, 7.1520934, -12.470615, -11.206358, 8.868341, -11.156143, -10.930391, 8.6175785, -11.966955, -10.282127, 8.873895, -12.083578, -10.930391, 8.6175785, -11.966955, -10.282127, 7.894554, -12.918314, -10.282127, 8.873895, -12.083578, -11.206358, 8.868341, -11.156143, -10.282127, 8.873895, -12.083578, -10.282127, 9.20027, -11.093346, -10.282127, 7.894554, -12.918314, -9.607803, 7.1520934, -12.470615, -9.43009, 7.889001, -12.524868, -10.282127, 8.873895, -12.083578, -10.282127, 7.894554, -12.918314, -9.43009, 7.889001, -12.524868, -9.298729, 6.8767676, -11.532499, -9.43009, 7.889001, -12.524868, -9.607803, 7.1520934, -12.470615, -10.282127, 9.20027, -11.093346, -10.282127, 8.873895, -12.083578, -9.666115, 8.6175785, -11.966955, -10.282127, 8.873895, -12.083578, -9.43009, 7.889001, -12.524868, -9.666115, 8.6175785, -11.966955, -9.298729, 6.8767676, -11.532499, -9.010374, 7.872555, -11.344322, -9.43009, 7.889001, -12.524868, -9.010374, 7.872555, -11.344322, -9.666115, 8.6175785, -11.966955, -9.43009, 7.889001, -12.524868, -9.678076, 7.1179185, -10.496557, -9.010374, 7.872555, -11.344322, -9.298729, 6.8767676, -11.532499, -9.678076, 7.1179185, -10.496557, -9.298729, 7.856108, -10.163559, -9.010374, 7.872555, -11.344322, -9.010374, 7.872555, -11.344322, -9.43009, 8.868341, -11.156143, -9.666115, 8.6175785, -11.966955, -10.282127, 9.20027, -11.093346, -9.666115, 8.6175785, -11.966955, -9.43009, 8.868341, -11.156143, -9.298729, 7.856108, -10.163559, -9.43009, 8.868341, -11.156143, -9.010374, 7.872555, -11.344322, -10.354536, 8.862788, -10.228495, -10.282127, 9.20027, -11.093346, -9.43009, 8.868341, -11.156143, -9.298729, 7.856108, -10.163559, -9.607803, 8.602841, -10.44273, -9.43009, 8.868341, -11.156143, -10.354536, 8.862788, -10.228495, -9.43009, 8.868341, -11.156143, -9.607803, 8.602841, -10.44273, -11.181795, 7.1520934, -12.470615, -10.571764, 6.544839, -11.595083, -10.354536, 6.904321, -12.966158, -11.181795, 7.1520934, -12.470615, -11.772175, 6.8767676, -11.532499, -10.571764, 6.544839, -11.595083, -10.354536, 6.904321, -12.966158, -10.571764, 6.544839, -11.595083, -9.298729, 6.8767676, -11.532499, -10.354536, 6.904321, -12.966158, -9.298729, 6.8767676, -11.532499, -9.607803, 7.1520934, -12.470615, -10.571764, 6.544839, -11.595083, -9.678076, 7.1179185, -10.496557, -9.298729, 6.8767676, -11.532499, -10.571764, 6.544839, -11.595083, -10.716581, 6.8492146, -10.098627, -9.678076, 7.1179185, -10.496557, -11.772175, 6.8767676, -11.532499, -10.716581, 6.8492146, -10.098627, -10.571764, 6.544839, -11.595083, -11.772175, 6.8767676, -11.532499, -11.561996, 7.1179185, -10.496557, -10.716581, 6.8492146, -10.098627, -7.722173, 5.52812, -11.093344, -8.820272, 5.8179703, -11.28964, -7.772368, 5.3862915, -11.30032, -7.722173, 5.52812, -11.093344, -8.73398, 5.93139, -11.093346, -8.820272, 5.8179703, -11.28964, -8.73398, 5.93139, -11.093346, -9.858778, 6.9177785, -11.150375, -8.820272, 5.8179703, -11.28964, -8.73398, 5.93139, -11.093346, -9.817341, 6.9173512, -11.093346, -9.858778, 6.9177785, -11.150375, -8.820272, 5.8179703, -11.28964, -9.858778, 6.9177785, -11.150375, -9.925848, 6.9184194, -11.128588, -7.772368, 5.3862915, -11.30032, -8.820272, 5.8179703, -11.28964, -8.959965, 5.6340637, -11.214668, -8.820272, 5.8179703, -11.28964, -9.925848, 6.9184194, -11.128588, -8.959965, 5.6340637, -11.214668, -7.772368, 5.3862915, -11.30032, -8.959965, 5.6340637, -11.214668, -7.8533216, 5.156889, -11.221289, -8.959965, 5.6340637, -11.214668, -9.925848, 6.9184194, -11.128588, -9.925848, 6.9184194, -11.058102, -7.8533216, 5.156889, -11.221289, -8.959965, 5.6340637, -11.214668, -8.959965, 5.6340637, -10.972021, -8.959965, 5.6340637, -11.214668, -9.925848, 6.9184194, -11.058102, -8.959965, 5.6340637, -10.972021, -7.8533216, 5.156889, -11.221289, -8.959965, 5.6340637, -10.972021, -7.8533216, 5.1568894, -10.965401, -8.959965, 5.6340637, -10.972021, -9.925848, 6.9184194, -11.058102, -9.858778, 6.9177785, -11.036314, -7.8533216, 5.1568894, -10.965401, -8.959965, 5.6340637, -10.972021, -8.820272, 5.8179703, -10.897049, -8.959965, 5.6340637, -10.972021, -9.858778, 6.9177785, -11.036314, -8.820272, 5.8179703, -10.897049, -8.820272, 5.8179703, -10.897049, -9.858778, 6.9177785, -11.036314, -9.817341, 6.9173512, -11.093346, -7.8533216, 5.1568894, -10.965401, -8.820272, 5.8179703, -10.897049, -7.772368, 5.386292, -10.88637, -8.820272, 5.8179703, -10.897049, -9.817341, 6.9173512, -11.093346, -8.73398, 5.93139, -11.093346, -7.772368, 5.386292, -10.88637, -8.820272, 5.8179703, -10.897049, -8.73398, 5.93139, -11.093346, -7.772368, 5.386292, -10.88637, -8.73398, 5.93139, -11.093346, -7.722173, 5.52812, -11.093344, -8.853594, 3.2268317, -11.093344, -7.8546023, 5.2839794, -10.867573, -8.425974, 3.226832, -10.469002, -8.853594, 3.2268317, -11.093344, -8.015869, 5.313242, -11.093344, -7.8546023, 5.2839794, -10.867573, -8.015869, 5.313242, -11.093344, -7.773436, 7.3500977, -10.993595, -7.8546023, 5.2839794, -10.867573, -8.015869, 5.313242, -11.093344, -7.8454185, 7.3575735, -11.093346, -7.773436, 7.3500977, -10.993595, -7.8546023, 5.2839794, -10.867573, -7.773436, 7.3500977, -10.993595, -7.6565986, 7.337709, -11.031616, -8.425974, 3.226832, -10.469002, -7.8546023, 5.2839794, -10.867573, -7.5933747, 5.236347, -10.953865, -7.8546023, 5.2839794, -10.867573, -7.6565986, 7.337709, -11.031616, -7.5933747, 5.236347, -10.953865, -8.425974, 3.226832, -10.469002, -7.5933747, 5.236347, -10.953865, -7.734348, 3.226832, -10.707375, -7.5933747, 5.236347, -10.953865, -7.6565986, 7.337709, -11.031616, -7.6565986, 7.337709, -11.155075, -7.734348, 3.226832, -10.707375, -7.5933747, 5.236347, -10.953865, -7.5935884, 5.236347, -11.232822, -7.5933747, 5.236347, -10.953865, -7.6565986, 7.337709, -11.155075, -7.5935884, 5.236347, -11.232822, -7.734348, 3.226832, -10.707375, -7.5935884, 5.236347, -11.232822, -7.734348, 3.2268317, -11.479313, -7.5935884, 5.236347, -11.232822, -7.6565986, 7.337709, -11.155075, -7.773436, 7.3500977, -11.193094, -7.734348, 3.2268317, -11.479313, -7.5935884, 5.236347, -11.232822, -7.8546023, 5.2839794, -11.319117, -7.5935884, 5.236347, -11.232822, -7.773436, 7.3500977, -11.193094, -7.8546023, 5.2839794, -11.319117, -7.8546023, 5.2839794, -11.319117, -7.773436, 7.3500977, -11.193094, -7.8454185, 7.3575735, -11.093346, -7.734348, 3.2268317, -11.479313, -7.8546023, 5.2839794, -11.319117, -8.425974, 3.2268314, -11.717686, -7.8546023, 5.2839794, -11.319117, -7.8454185, 7.3575735, -11.093346, -8.015869, 5.313242, -11.093344, -8.425974, 3.2268314, -11.717686, -7.8546023, 5.2839794, -11.319117, -8.015869, 5.313242, -11.093344, -8.425974, 3.2268314, -11.717686, -8.015869, 5.313242, -11.093344, -8.853594, 3.2268317, -11.093344) + +[sub_resource type="PlaneMesh" id="PlaneMesh_7ny4s"] +resource_local_to_scene = true +size = Vector2(16.1, 12.3675) + +[sub_resource type="BoxShape3D" id="BoxShape3D_agj3o"] +resource_local_to_scene = true +size = Vector3(16.1, 4, 12.3675) + +[sub_resource type="BoxShape3D" id="BoxShape3D_l6210"] +resource_local_to_scene = true +size = Vector3(16.1, 4.8, 12.3675) + +[sub_resource type="PlaneMesh" id="PlaneMesh_6upbg"] +resource_local_to_scene = true +size = Vector2(10000, 10000) + +[sub_resource type="BoxShape3D" id="BoxShape3D_umo15"] +resource_local_to_scene = true +size = Vector3(17, 2, 80) + +[sub_resource type="BoxShape3D" id="BoxShape3D_e4lmg"] +resource_local_to_scene = true +size = Vector3(42, 2, 80) + +[sub_resource type="BoxShape3D" id="BoxShape3D_eb3g5"] +resource_local_to_scene = true +size = Vector3(48, 2, 18) + +[sub_resource type="BoxShape3D" id="BoxShape3D_wsqap"] +resource_local_to_scene = true +size = Vector3(48, 2, 18) + +[sub_resource type="BoxShape3D" id="BoxShape3D_1fdkm"] +resource_local_to_scene = true +size = Vector3(17, 5, 80) + +[sub_resource type="BoxShape3D" id="BoxShape3D_y2c0j"] +resource_local_to_scene = true +size = Vector3(42, 5, 80) + +[sub_resource type="BoxShape3D" id="BoxShape3D_uwwqy"] +resource_local_to_scene = true +size = Vector3(48, 5, 18) + +[sub_resource type="BoxShape3D" id="BoxShape3D_hf28p"] +resource_local_to_scene = true +size = Vector3(48, 5, 18) + [node name="Main" type="Node3D" unique_id=704196381] script = ExtResource("3_main") fish_catalog = ExtResource("6_pool") @@ -118,26 +175,34 @@ mouse_filter = 2 unique_name_in_owner = true script = ExtResource("39_fonts") -[node name="WorldTimeService" type="Node" parent="."] +[node name="WorldTimeService" type="Node" parent="." unique_id=1774610576] unique_name_in_owner = true script = ExtResource("47_world_time") -[node name="WorldTimeVisualController" type="Node" parent="."] +[node name="WorldTimeVisualController" type="Node" parent="." unique_id=1861087008] unique_name_in_owner = true script = ExtResource("48_world_time_visuals") -[node name="NetworkWorldTimeService" type="Node" parent="."] +[node name="NetworkWorldTimeService" type="Node" parent="." unique_id=1136962511] unique_name_in_owner = true script = ExtResource("49_network_world_time") -[node name="WorldWeatherService" type="Node" parent="."] +[node name="WorldWeatherService" type="Node" parent="." unique_id=1812156280] unique_name_in_owner = true script = ExtResource("50_world_weather") -[node name="NetworkWorldWeatherService" type="Node" parent="."] +[node name="NetworkWorldWeatherService" type="Node" parent="." unique_id=266238117] unique_name_in_owner = true script = ExtResource("51_network_world_weather") +[node name="PlayerJobService" type="Node" parent="." unique_id=2062127535] +unique_name_in_owner = true +script = ExtResource("52_player_jobs") + +[node name="NetworkJobService" type="Node" parent="." unique_id=556458360] +unique_name_in_owner = true +script = ExtResource("53_network_jobs") + [node name="PlayerDataRoot" type="Node" parent="." unique_id=281868360] unique_name_in_owner = true script = ExtResource("37_data_root") @@ -150,6 +215,10 @@ script = ExtResource("38_identity_backup") unique_name_in_owner = true script = ExtResource("17_network_session") +[node name="DiscoveryClient" type="Node" parent="." unique_id=401971194] +unique_name_in_owner = true +script = ExtResource("57_discovery") + [node name="NetworkProfilePreferences" type="Node" parent="." unique_id=2119445545] unique_name_in_owner = true script = ExtResource("18_network_profile") @@ -210,11 +279,11 @@ script = ExtResource("23_network_shop") unique_name_in_owner = true script = ExtResource("24_network_item") -[node name="NetworkFishShowcaseService" type="Node" parent="."] +[node name="NetworkFishShowcaseService" type="Node" parent="." unique_id=148288598] unique_name_in_owner = true script = ExtResource("45_fish_showcase") -[node name="NetworkSurfaceDrawingService" type="Node" parent="."] +[node name="NetworkSurfaceDrawingService" type="Node" parent="." unique_id=80122665] unique_name_in_owner = true script = ExtResource("46_surface_drawing") @@ -235,15 +304,79 @@ unique_name_in_owner = true script = ExtResource("36_player_list") [node name="TestWorld" parent="." unique_id=1334932296 instance=ExtResource("1_world")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0024132729, 0, -0.00093126297) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.4475808, 0, -1.8959346) + +[node name="Sun" parent="TestWorld/Environment" index="1"] +transform = Transform3D(0.8480481, 0.4287137, -0.31147876, 0, 0.5877853, 0.809017, 0.52991927, -0.68608534, 0.49847022, 0, 0, 0) + +[node name="Shape" parent="TestWorld/Regions/StarterIslandRegion/Terrain/Collision" index="0" unique_id=674742331] +shape = SubResource("ConcavePolygonShape3D_hifdc") + +[node name="Pond" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies" index="0" unique_id=995477450] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.4, 2.51, 2.1) + +[node name="VisualWater" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond" index="0" unique_id=1034265960] +mesh = SubResource("PlaneMesh_7ny4s") + +[node name="Shape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond/FishingRegion" index="0" unique_id=1185360438] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0) +shape = SubResource("BoxShape3D_agj3o") + +[node name="Shape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond/RecoveryRegion" index="0" unique_id=1718808476] +shape = SubResource("BoxShape3D_l6210") + +[node name="VisualWater" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean" index="0" unique_id=1176408937] +mesh = SubResource("PlaneMesh_6upbg") + +[node name="WestShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="0" unique_id=357406062] +shape = SubResource("BoxShape3D_umo15") + +[node name="EastShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="1" unique_id=772224738] +shape = SubResource("BoxShape3D_e4lmg") + +[node name="NorthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="2" unique_id=646147646] +shape = SubResource("BoxShape3D_eb3g5") + +[node name="SouthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="3" unique_id=894083854] +shape = SubResource("BoxShape3D_wsqap") + +[node name="WestShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="0" unique_id=1183368645] +shape = SubResource("BoxShape3D_1fdkm") + +[node name="EastShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="1" unique_id=1742419808] +shape = SubResource("BoxShape3D_y2c0j") + +[node name="NorthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="2" unique_id=1844920260] +shape = SubResource("BoxShape3D_uwwqy") + +[node name="SouthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="3" unique_id=1316495770] +shape = SubResource("BoxShape3D_hf28p") + +[node name="FishingShopWorld" parent="TestWorld/Regions/StarterIslandRegion/Interactables" index="0" unique_id=1255373524] +transform = Transform3D(-0.9976046, 0, -0.06917416, 0, 1, 0, 0.06917416, 0, -0.9976046, 7.3292284, 3.4012775, 14.888193) + +[node name="BelowWorldFailsafe" parent="TestWorld/Safety" index="0"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, -7, 0) + +[node name="North" parent="TestWorld/WorldBounds" index="0"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 5, 63.225395) + +[node name="South" parent="TestWorld/WorldBounds" index="1"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 5, -63.225395) + +[node name="West" parent="TestWorld/WorldBounds" index="2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -72.4386, 5, 0) + +[node name="East" parent="TestWorld/WorldBounds" index="3"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 96.4386, 5, 0) [node name="Players" type="Node3D" parent="." unique_id=550630144] -[node name="SurfaceDrawings" type="Node3D" parent="."] - [node name="Player" parent="Players" unique_id=485429332 instance=ExtResource("2_player")] unique_name_in_owner = true -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 16) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.3999481, 12.07081) + +[node name="SurfaceDrawings" type="Node3D" parent="." unique_id=234671307] [node name="FishingSpot" parent="." unique_id=1461328864 instance=ExtResource("4_spot")] unique_name_in_owner = true @@ -261,12 +394,30 @@ script = ExtResource("9_save") unique_name_in_owner = true script = ExtResource("10_settings") +[node name="ControllerMappingManager" type="Node" parent="." unique_id=1198390067] +unique_name_in_owner = true +script = ExtResource("56_controller_mapping") + [node name="TitleMusic" type="AudioStreamPlayer" parent="." unique_id=1292939956] unique_name_in_owner = true stream = ExtResource("13_title_music") volume_db = -80.0 bus = &"Music" +[node name="DuskMusic" type="AudioStreamPlayer" parent="." unique_id=992744241] +unique_name_in_owner = true +stream = ExtResource("dusk_music") +bus = &"Music" + +[node name="ShorelineAmbience" type="Node" parent="." unique_id=1948037177] +unique_name_in_owner = true +script = ExtResource("54_shoreline_ambience") + +[node name="WavesAudio" type="AudioStreamPlayer" parent="ShorelineAmbience" unique_id=193530658] +unique_name_in_owner = true +stream = ExtResource("55_waves") +bus = &"Environment" + [node name="WorldPixelationPostprocess" parent="." unique_id=344378000 instance=ExtResource("16_world_pixelation")] unique_name_in_owner = true @@ -292,5 +443,5 @@ unique_name_in_owner = true [node name="PixelationResetOverlay" parent="." unique_id=291636041 instance=ExtResource("15_reset")] unique_name_in_owner = true -[node name="netfishing_base_character" parent="." unique_id=163901730 instance=ExtResource("44_4pcu1")] -transform = Transform3D(-0.9976046, 0, -0.06917416, 0, 1, 0, 0.06917416, 0, -0.9976046, 8.795597, 4.184117, 7.4327927) +[editable path="TestWorld"] +[editable path="TestWorld/Regions/StarterIslandRegion"] diff --git a/main/texture_sampling_policy.gd b/main/texture_sampling_policy.gd new file mode 100644 index 0000000..3d041d0 --- /dev/null +++ b/main/texture_sampling_policy.gd @@ -0,0 +1,74 @@ +extends Node + +## Enforces NETfishing's nearest-neighbor-only artwork policy at runtime. +## +## Godot uses separate texture-filter enums for CanvasItem and 3D materials. +## Applying both here prevents imported models, dynamically created controls, +## and Sprite3D presentations from silently falling back to linear sampling. + + +func _enter_tree() -> void: + get_tree().node_added.connect(_on_node_added) + call_deferred("_enforce_existing_tree") + + +func _on_node_added(node: Node) -> void: + enforce_node(node) + # Some runtime presenters assign their mesh or material immediately after + # add_child(). Recheck once deferred so those resources are covered too. + call_deferred("_enforce_deferred_node", weakref(node)) + + +func _enforce_existing_tree() -> void: + enforce_subtree(get_tree().root) + + +func _enforce_deferred_node(node_ref: WeakRef) -> void: + var node := node_ref.get_ref() as Node + if node != null: + enforce_node(node) + + +static func enforce_subtree(node: Node) -> void: + enforce_node(node) + for child: Node in node.get_children(): + enforce_subtree(child) + + +static func enforce_node(node: Node) -> void: + if node is CanvasItem: + (node as CanvasItem).texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + if node is SpriteBase3D: + (node as SpriteBase3D).texture_filter = ( + BaseMaterial3D.TEXTURE_FILTER_NEAREST + ) + if node is GeometryInstance3D: + var geometry := node as GeometryInstance3D + enforce_material(geometry.material_override) + enforce_material(geometry.material_overlay) + if node is MeshInstance3D: + enforce_mesh((node as MeshInstance3D).mesh) + if node is MultiMeshInstance3D: + var multimesh := (node as MultiMeshInstance3D).multimesh + if multimesh != null: + enforce_mesh(multimesh.mesh) + if node is GPUParticles3D: + var particles := node as GPUParticles3D + for pass_index: int in particles.draw_passes: + enforce_mesh(particles.get_draw_pass_mesh(pass_index)) + if node is CPUParticles3D: + enforce_mesh((node as CPUParticles3D).mesh) + + +static func enforce_mesh(mesh: Mesh) -> void: + if mesh == null: + return + for surface_index: int in mesh.get_surface_count(): + enforce_material(mesh.surface_get_material(surface_index)) + + +static func enforce_material(material: Material) -> void: + if material is BaseMaterial3D: + (material as BaseMaterial3D).texture_filter = ( + BaseMaterial3D.TEXTURE_FILTER_NEAREST + ) diff --git a/main/texture_sampling_policy.gd.uid b/main/texture_sampling_policy.gd.uid new file mode 100644 index 0000000..820783e --- /dev/null +++ b/main/texture_sampling_policy.gd.uid @@ -0,0 +1 @@ +uid://dwd4wfn8bwk5i diff --git a/network/direct_enet_transport.gd b/network/direct_enet_transport.gd index fdfa048..067b86c 100644 --- a/network/direct_enet_transport.gd +++ b/network/direct_enet_transport.gd @@ -40,3 +40,22 @@ func connect_to_route(route: ConnectionRoute) -> Error: _peer = enet_peer _route_description = endpoint.normalized_display return OK + + +func send_raw_packet( + destination_address: String, + destination_port: int, + packet: PackedByteArray, +) -> bool: + var enet_peer := _peer as ENetMultiplayerPeer + if ( + enet_peer == null + or enet_peer.host == null + or destination_address.is_empty() + or destination_port < 1 + or destination_port > 65535 + or packet.is_empty() + ): + return false + enet_peer.host.socket_send(destination_address, destination_port, packet) + return true diff --git a/network/discovery_client.gd b/network/discovery_client.gd new file mode 100644 index 0000000..bf15bbf --- /dev/null +++ b/network/discovery_client.gd @@ -0,0 +1,1089 @@ +class_name DiscoveryClient +extends Node + +signal rooms_updated(rooms: Array[Dictionary]) +signal browse_status_changed(message: String, is_error: bool) +signal host_settings_changed(room_name: String, discoverable: bool) +signal host_status_changed(message: String, is_error: bool) +signal host_state_changed(state: int) +signal public_join_prepared(endpoint: String) +signal public_join_status_changed(message: String, is_error: bool) +signal public_join_state_changed(state: int) + +const BASE_URL_SETTING: String = "network/discovery/base_url" +const BASE_URL_ENVIRONMENT: String = "NETFISHING_DISCOVERY_URL" +const SETTINGS_PATH: String = "user://network_discovery.cfg" +const DEFAULT_ROOM_NAME: String = "Player's Server" +const LEGACY_DEFAULT_ROOM_NAME: String = "NETfishing Room" +const LEGACY_DEDICATED_DEFAULT_ROOM_NAME: String = ( + "NETfishing Dedicated Server" +) +const ROOM_NAME_SUFFIX: String = "'s Server" +const MAX_ROOM_NAME_LENGTH: int = 48 +const HEARTBEAT_INTERVAL_SECONDS: float = 15.0 +const REQUEST_TIMEOUT_SECONDS: float = 8.0 +const TRAVERSAL_POLL_INTERVAL_SECONDS: float = 1.0 +const JOIN_PROBE_INTERVAL_SECONDS: float = 0.35 +const TRAVERSAL_PACKET_PREFIX: String = "NETFISHING_TRAVERSAL_V1 " +const UPNP_MAPPING_DURATION_SECONDS: int = 3600 +const UPNP_RENEW_INTERVAL_SECONDS: float = 2700.0 +const UPNP_RETRY_INTERVAL_SECONDS: float = 300.0 + +enum HostState { + UNAVAILABLE, + CLOSED, + PRIVATE, + OPENING_PORT, + REGISTERING, + VERIFYING, + PUBLIC, + ERROR, +} + +enum PublicJoinState { + IDLE, + REQUESTING_ROUTE, + CONNECTING, + ERROR, +} + +enum HostRequestKind { + NONE, + CREATE, + UPDATE, + DELETE, +} + +var _session: NetworkSession +var _base_url: String = "" +var _room_name: String = DEFAULT_ROOM_NAME +var _room_name_uses_default: bool = true +var _host_settings_persistence_enabled: bool = true +var _discoverable: bool = false +var _host_status_message: String = "" +var _host_status_is_error: bool = false +var _host_state: HostState = HostState.CLOSED +var _public_join_state: PublicJoinState = PublicJoinState.IDLE +var _lease_room_id: String = "" +var _lease_token: String = "" +var _host_request_kind: HostRequestKind = HostRequestKind.NONE +var _host_request_in_flight: bool = false +var _host_sync_queued: bool = false +var _browse_request_in_flight: bool = false +var _host_request: HTTPRequest +var _browse_request: HTTPRequest +var _heartbeat: Timer +var _traversal_request: HTTPRequest +var _join_request: HTTPRequest +var _traversal_timer: Timer +var _join_probe_timer: Timer +var _host_verified: bool = false +var _traversal_host: String = "" +var _traversal_port: int = 0 +var _host_verification_token: String = "" +var _join_request_in_flight: bool = false +var _pending_join_endpoint: String = "" +var _pending_join_token: String = "" +var _pending_join_room_id: String = "" +var _preserve_pending_join_on_inactive: bool = false +var _upnp_thread: Thread +var _upnp_mapping_in_progress: bool = false +var _upnp_operation_is_renewal: bool = false +var _upnp_renew_timer: Timer +var _upnp: UPNP +var _upnp_mapped_port: int = 0 + + +func _ready() -> void: + _host_request = HTTPRequest.new() + _host_request.name = "HostLeaseRequest" + _host_request.timeout = REQUEST_TIMEOUT_SECONDS + add_child(_host_request) + _host_request.request_completed.connect(_on_host_request_completed) + + _browse_request = HTTPRequest.new() + _browse_request.name = "RoomBrowseRequest" + _browse_request.timeout = REQUEST_TIMEOUT_SECONDS + add_child(_browse_request) + _browse_request.request_completed.connect(_on_browse_request_completed) + + _traversal_request = HTTPRequest.new() + _traversal_request.name = "HostTraversalRequest" + _traversal_request.timeout = REQUEST_TIMEOUT_SECONDS + add_child(_traversal_request) + _traversal_request.request_completed.connect( + _on_traversal_request_completed + ) + + _join_request = HTTPRequest.new() + _join_request.name = "PublicJoinPreparationRequest" + _join_request.timeout = REQUEST_TIMEOUT_SECONDS + add_child(_join_request) + _join_request.request_completed.connect(_on_join_request_completed) + + _heartbeat = Timer.new() + _heartbeat.name = "HostLeaseHeartbeat" + _heartbeat.wait_time = HEARTBEAT_INTERVAL_SECONDS + _heartbeat.one_shot = false + add_child(_heartbeat) + _heartbeat.timeout.connect(_on_heartbeat_timeout) + + _upnp_renew_timer = Timer.new() + _upnp_renew_timer.name = "HostUPnPRenewal" + _upnp_renew_timer.one_shot = true + add_child(_upnp_renew_timer) + _upnp_renew_timer.timeout.connect(_on_upnp_renew_timer_timeout) + + _traversal_timer = Timer.new() + _traversal_timer.name = "HostTraversalPoll" + _traversal_timer.wait_time = TRAVERSAL_POLL_INTERVAL_SECONDS + _traversal_timer.one_shot = false + add_child(_traversal_timer) + _traversal_timer.timeout.connect(_on_traversal_timer_timeout) + + _join_probe_timer = Timer.new() + _join_probe_timer.name = "PublicJoinTraversalProbe" + _join_probe_timer.wait_time = JOIN_PROBE_INTERVAL_SECONDS + _join_probe_timer.one_shot = false + add_child(_join_probe_timer) + _join_probe_timer.timeout.connect(_send_pending_join_probe) + _load_settings() + _base_url = _configured_base_url() + + +func setup(session: NetworkSession) -> void: + _session = session + if _room_name_uses_default: + _room_name = _default_room_name(_session.get_local_display_name()) + _save_settings() + _session.set_session_display_name(_room_name) + if not _session.state_changed.is_connected(_on_session_state_changed): + _session.state_changed.connect(_on_session_state_changed) + if not _session.peer_count_changed.is_connected(_on_peer_count_changed): + _session.peer_count_changed.connect(_on_peer_count_changed) + if not _session.host_openness_changed.is_connected(_on_host_openness_changed): + _session.host_openness_changed.connect(_on_host_openness_changed) + host_settings_changed.emit(_room_name, _discoverable) + if not is_configured(): + _set_host_state(HostState.UNAVAILABLE) + _set_host_status("Room discovery is not configured in this build.", true) + elif _session.is_open_host(): + _set_host_state(HostState.PRIVATE) + _set_host_status("Room is open but unlisted.", false) + else: + _set_host_state(HostState.CLOSED) + _set_host_status("Open the game before listing it publicly.", false) + + +func is_configured() -> bool: + return not _base_url.is_empty() + + +func get_base_url() -> String: + return _base_url + + +func set_base_url_override(value: String) -> bool: + var normalized: String = value.strip_edges() + while normalized.ends_with("/"): + normalized = normalized.left(normalized.length() - 1) + if not normalized.is_empty() and not ( + normalized.begins_with("https://") + or normalized.begins_with("http://") + ): + return false + _base_url = normalized + return true + + +func get_room_name() -> String: + return _room_name + + +func is_discoverable() -> bool: + return _discoverable + + +func get_host_status_message() -> String: + return _host_status_message + + +func host_status_is_error() -> bool: + return _host_status_is_error + + +func get_host_state() -> HostState: + return _host_state + + +func get_public_join_state() -> PublicJoinState: + return _public_join_state + + +func configure_dedicated_runtime(room_name: String) -> bool: + _host_settings_persistence_enabled = false + return set_room_name(room_name) + + +func set_room_name(value: String) -> bool: + var cleaned: String = _sanitize_room_name(value) + if cleaned.is_empty(): + _set_host_state(HostState.ERROR) + _set_host_status("Room name cannot be empty.", true) + return false + if cleaned == _room_name: + if _room_name_uses_default: + _room_name_uses_default = false + _save_settings() + return true + _room_name = cleaned + _room_name_uses_default = false + _save_settings() + if _session != null: + _session.set_session_display_name(_room_name) + host_settings_changed.emit(_room_name, _discoverable) + if _discoverable: + _synchronize_host_lease() + return true + + +func set_discoverable(enabled: bool) -> bool: + if enabled and ( + _session == null + or not _session.is_open_host() + or not is_configured() + ): + _set_host_state(HostState.ERROR) + _set_host_status( + "Open the game before enabling discovery." + if is_configured() + else "Room discovery is not configured in this build.", + true, + ) + return false + if _discoverable == enabled: + return true + _discoverable = enabled + host_settings_changed.emit(_room_name, _discoverable) + if enabled: + _heartbeat.start() + _set_host_state( + HostState.REGISTERING + if _session.is_dedicated_host() + else HostState.OPENING_PORT + ) + _set_host_status("Opening public room…", false) + if _session.is_dedicated_host(): + _synchronize_host_lease() + else: + _begin_upnp_mapping(_session.get_host_port()) + else: + _heartbeat.stop() + _upnp_renew_timer.stop() + _traversal_timer.stop() + _remove_host_lease() + _begin_upnp_cleanup() + _set_host_state(HostState.PRIVATE) + _set_host_status("Room is open but unlisted.", false) + return true + + +func is_public_join_preparing() -> bool: + return _join_request_in_flight + + +func preserve_public_join_for_session_switch() -> bool: + _preserve_pending_join_on_inactive = not _pending_join_token.is_empty() + return _preserve_pending_join_on_inactive + + +func cancel_pending_public_join() -> void: + _preserve_pending_join_on_inactive = false + _set_public_join_state(PublicJoinState.IDLE) + _clear_pending_join() + + +func prepare_public_join(room: Dictionary) -> bool: + if _join_request_in_flight: + return false + if _session == null or not is_configured(): + _set_public_join_state(PublicJoinState.ERROR) + return false + if is_own_room(room): + _set_public_join_state(PublicJoinState.ERROR) + public_join_status_changed.emit( + "You are already hosting this room.", true + ) + return false + var endpoint: String = room_endpoint(room) + var room_id: String = str(room.get("room_id", "")).strip_edges() + if endpoint.is_empty() or room_id.is_empty(): + _set_public_join_state(PublicJoinState.ERROR) + return false + _pending_join_endpoint = endpoint + _pending_join_room_id = room_id + _pending_join_token = "" + var url: String = "%s/v1/rooms/%s/join-attempts" % [ + _base_url, + room_id.uri_encode(), + ] + var error: Error = _join_request.request( + url, + PackedStringArray(), + HTTPClient.METHOD_POST, + "", + ) + if error != OK: + _set_public_join_state(PublicJoinState.ERROR) + _clear_pending_join() + return false + _join_request_in_flight = true + _set_public_join_state(PublicJoinState.REQUESTING_ROUTE) + public_join_status_changed.emit("Opening a route to the room…", false) + return true + + +func request_rooms() -> bool: + if not is_configured(): + rooms_updated.emit([]) + browse_status_changed.emit( + "Public room discovery is not configured in this build.", true + ) + return false + if _browse_request_in_flight: + return true + var game_version: String = str( + ProjectSettings.get_setting("application/config/version", "unknown") + ) + var url: String = "%s/v1/rooms?game_version=%s&protocol_version=%d" % [ + _base_url, + game_version.uri_encode(), + NetworkProtocol.PROTOCOL_VERSION, + ] + var error: Error = _browse_request.request(url) + if error != OK: + rooms_updated.emit([]) + browse_status_changed.emit("Could not request public rooms.", true) + return false + _browse_request_in_flight = true + browse_status_changed.emit("Looking for public rooms…", false) + return true + + +func room_endpoint(room: Dictionary) -> String: + var address: String = str(room.get("address", "")).strip_edges() + var port: int = int(room.get("port", 0)) + if address.is_empty() or port < 1 or port > 65535: + return "" + if ":" in address and not address.begins_with("["): + address = "[%s]" % address + return "%s:%d" % [address, port] + + +func is_own_room(room: Dictionary) -> bool: + return ( + not _lease_room_id.is_empty() + and str(room.get("room_id", "")) == _lease_room_id + ) + + +func _configured_base_url() -> String: + var value: String = OS.get_environment(BASE_URL_ENVIRONMENT).strip_edges() + if value.is_empty(): + value = str(ProjectSettings.get_setting(BASE_URL_SETTING, "")).strip_edges() + while value.ends_with("/"): + value = value.left(value.length() - 1) + if not value.is_empty() and not ( + value.begins_with("https://") or value.begins_with("http://") + ): + push_warning("Ignored invalid NETfishing discovery URL.") + return "" + return value + + +func _on_heartbeat_timeout() -> void: + _synchronize_host_lease() + # NAT bindings can change during a long host session. Re-sending the signed + # probe lets discovery refresh the authoritative public endpoint in place. + _send_host_verification_probe() + + +func _synchronize_host_lease() -> void: + if not _should_advertise(): + if not _lease_room_id.is_empty(): + _remove_host_lease() + return + if _host_request_in_flight: + _host_sync_queued = true + return + var method: HTTPClient.Method = HTTPClient.METHOD_POST + var url: String = "%s/v1/rooms" % _base_url + var headers := PackedStringArray(["Content-Type: application/json"]) + _host_request_kind = HostRequestKind.CREATE + if not _lease_room_id.is_empty(): + method = HTTPClient.METHOD_PUT + url = "%s/v1/rooms/%s" % [_base_url, _lease_room_id.uri_encode()] + headers.append("Authorization: Bearer %s" % _lease_token) + _host_request_kind = HostRequestKind.UPDATE + elif _host_state != HostState.REGISTERING: + _set_host_state(HostState.REGISTERING) + _start_host_request(url, headers, method, JSON.stringify(_host_payload())) + + +func _remove_host_lease() -> void: + if _lease_room_id.is_empty(): + return + if _host_request_in_flight: + _host_sync_queued = true + return + var url: String = "%s/v1/rooms/%s" % [ + _base_url, _lease_room_id.uri_encode() + ] + var headers := PackedStringArray([ + "Authorization: Bearer %s" % _lease_token, + ]) + _host_request_kind = HostRequestKind.DELETE + _start_host_request(url, headers, HTTPClient.METHOD_DELETE, "") + + +func _start_host_request( + url: String, + headers: PackedStringArray, + method: HTTPClient.Method, + body: String, +) -> void: + var error: Error = _host_request.request(url, headers, method, body) + if error != OK: + _host_request_kind = HostRequestKind.NONE + _set_host_state(HostState.ERROR) + _set_host_status("Could not contact room discovery.", true) + return + _host_request_in_flight = true + + +func _host_payload() -> Dictionary: + return { + "room_name": _room_name, + "port": _session.get_host_port(), + "current_players": _session.get_player_count(), + "max_players": _session.get_session_max_players(), + "game_version": str( + ProjectSettings.get_setting("application/config/version", "unknown") + ), + "protocol_version": NetworkProtocol.PROTOCOL_VERSION, + } + + +func _should_advertise() -> bool: + return ( + _discoverable + and is_configured() + and _session != null + and _session.is_open_host() + and ( + not _upnp_mapping_in_progress + or _upnp_operation_is_renewal + ) + ) + + +func _on_host_request_completed( + result: int, + response_code: int, + _headers: PackedStringArray, + body: PackedByteArray, +) -> void: + var completed_kind: HostRequestKind = _host_request_kind + _host_request_kind = HostRequestKind.NONE + _host_request_in_flight = false + var response: Dictionary = _parse_response_dictionary(body) + var transport_ok: bool = result == HTTPRequest.RESULT_SUCCESS + match completed_kind: + HostRequestKind.CREATE: + if transport_ok and response_code == HTTPClient.RESPONSE_CREATED: + var room: Dictionary = response.get("room", {}) + _lease_room_id = str(room.get("room_id", "")) + _lease_token = str(response.get("lease_token", "")) + if _lease_room_id.is_empty() or _lease_token.is_empty(): + _clear_lease() + _set_host_state(HostState.ERROR) + _set_host_status("Discovery returned an invalid lease.", true) + else: + _apply_traversal_response(response) + _host_verified = bool(room.get("verified", false)) + _traversal_timer.start() + _send_host_verification_probe() + _set_host_state( + HostState.PUBLIC + if _host_verified + else HostState.VERIFYING + ) + _set_host_status( + "Room is listed publicly." + if _host_verified + else "Checking the public route…", + false, + ) + else: + _set_host_state(HostState.ERROR) + _set_host_status(_request_failure(response), true) + HostRequestKind.UPDATE: + if transport_ok and response_code == HTTPClient.RESPONSE_OK: + var room: Dictionary = response.get("room", {}) + _host_verified = bool(room.get("verified", false)) + _set_host_state( + HostState.PUBLIC + if _host_verified + else HostState.VERIFYING + ) + _set_host_status( + "Room is listed publicly." + if _host_verified + else "Checking the public route…", + false, + ) + elif response_code in [ + HTTPClient.RESPONSE_UNAUTHORIZED, + HTTPClient.RESPONSE_NOT_FOUND, + ]: + _clear_lease() + _host_sync_queued = true + else: + _set_host_state(HostState.ERROR) + _set_host_status(_request_failure(response), true) + HostRequestKind.DELETE: + _clear_lease() + if not transport_ok or response_code not in [ + HTTPClient.RESPONSE_NO_CONTENT, + HTTPClient.RESPONSE_NOT_FOUND, + ]: + _set_host_state(HostState.ERROR) + _set_host_status(_request_failure(response), true) + _: + pass + if not _should_advertise() and not _lease_room_id.is_empty(): + _host_sync_queued = true + if _host_sync_queued: + _host_sync_queued = false + call_deferred("_synchronize_host_lease") + + +func _on_join_request_completed( + result: int, + response_code: int, + _headers: PackedStringArray, + body: PackedByteArray, +) -> void: + _join_request_in_flight = false + var response: Dictionary = _parse_response_dictionary(body) + if ( + result != HTTPRequest.RESULT_SUCCESS + or response_code != HTTPClient.RESPONSE_CREATED + ): + _set_public_join_state(PublicJoinState.ERROR) + public_join_status_changed.emit(_request_failure(response), true) + _clear_pending_join() + return + _pending_join_token = str(response.get("join_token", "")) + _apply_traversal_response(response) + if ( + _pending_join_token.is_empty() + or _traversal_host.is_empty() + or _traversal_port < 1 + ): + _set_public_join_state(PublicJoinState.ERROR) + public_join_status_changed.emit( + "Discovery returned an invalid traversal route.", true + ) + _clear_pending_join() + return + _set_public_join_state(PublicJoinState.CONNECTING) + public_join_status_changed.emit("Connecting…", false) + public_join_prepared.emit(_pending_join_endpoint) + + +func _on_traversal_timer_timeout() -> void: + if not _should_advertise() or _lease_room_id.is_empty(): + _traversal_timer.stop() + return + if not _host_verified: + _send_host_verification_probe() + _synchronize_host_lease() + return + if _traversal_request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED: + return + var url: String = "%s/v1/rooms/%s/join-attempts" % [ + _base_url, + _lease_room_id.uri_encode(), + ] + var headers := PackedStringArray([ + "Authorization: Bearer %s" % _lease_token, + ]) + var error: Error = _traversal_request.request(url, headers) + if error != OK: + _set_host_state(HostState.ERROR) + _set_host_status("Could not check incoming public connections.", true) + + +func _on_traversal_request_completed( + result: int, + response_code: int, + _headers: PackedStringArray, + body: PackedByteArray, +) -> void: + if result != HTTPRequest.RESULT_SUCCESS or response_code != HTTPClient.RESPONSE_OK: + return + var response: Dictionary = _parse_response_dictionary(body) + var endpoints: Variant = response.get("endpoints", []) + if typeof(endpoints) != TYPE_ARRAY: + return + for value: Variant in endpoints: + if typeof(value) != TYPE_DICTIONARY: + continue + var endpoint: Dictionary = value + var address: String = str(endpoint.get("address", "")) + var port: int = int(endpoint.get("port", 0)) + if address.is_empty() or port < 1 or port > 65535: + continue + var punch: PackedByteArray = "NETFISHING_PUNCH_V1".to_utf8_buffer() + for _attempt: int in 3: + _session.send_traversal_packet(address, port, punch) + + +func _apply_traversal_response(response: Dictionary) -> void: + var traversal: Variant = response.get("traversal", {}) + if typeof(traversal) != TYPE_DICTIONARY: + return + var details: Dictionary = traversal + _traversal_host = str(details.get("host", "")).strip_edges() + _traversal_port = int(details.get("port", 0)) + _host_verification_token = str( + details.get("verification_token", "") + ) + + +func _send_host_verification_probe() -> void: + if ( + _session == null + or _lease_room_id.is_empty() + or _host_verification_token.is_empty() + ): + return + _send_traversal_probe({ + "kind": "host", + "room_id": _lease_room_id, + "token": _host_verification_token, + }) + + +func _send_pending_join_probe() -> void: + if ( + _session == null + or _pending_join_token.is_empty() + or _session.state not in [ + NetworkSession.State.CONNECTING, + NetworkSession.State.AUTHENTICATING, + ] + ): + _join_probe_timer.stop() + return + _send_traversal_probe({ + "kind": "join", + "room_id": _pending_join_room_id, + "token": _pending_join_token, + }) + + +func _send_traversal_probe(payload: Dictionary) -> void: + if _traversal_host.is_empty() or _traversal_port < 1: + return + var packet: PackedByteArray = ( + TRAVERSAL_PACKET_PREFIX + JSON.stringify(payload) + ).to_utf8_buffer() + _session.send_traversal_packet(_traversal_host, _traversal_port, packet) + + +func _begin_upnp_mapping(port: int, is_renewal: bool = false) -> void: + if _upnp_mapping_in_progress or port < 1: + if not is_renewal: + _synchronize_host_lease() + return + _upnp_mapping_in_progress = true + _upnp_operation_is_renewal = is_renewal + _upnp_thread = Thread.new() + var error: Error = _upnp_thread.start( + _configure_upnp_mapping.bind(port) + ) + if error != OK: + _upnp_thread = null + _upnp_mapping_in_progress = false + _upnp_operation_is_renewal = false + if is_renewal: + _schedule_upnp_renewal(UPNP_RETRY_INTERVAL_SECONDS) + _synchronize_host_lease() + + +func _configure_upnp_mapping(port: int) -> Dictionary: + var upnp := UPNP.new() + var discovery_result: int = upnp.discover() + if discovery_result != UPNP.UPNP_RESULT_SUCCESS: + return {"success": false, "upnp": upnp, "port": port} + var mapping_result: int = upnp.add_port_mapping( + port, + port, + "NETfishing", + "UDP", + UPNP_MAPPING_DURATION_SECONDS, + ) + if mapping_result == UPNP.UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED: + mapping_result = upnp.add_port_mapping( + port, port, "NETfishing", "UDP", 0 + ) + return { + "success": mapping_result == UPNP.UPNP_RESULT_SUCCESS, + "upnp": upnp, + "port": port, + } + + +func _process(_delta: float) -> void: + if _upnp_thread == null or _upnp_thread.is_alive(): + return + var was_renewal: bool = _upnp_operation_is_renewal + var result: Variant = _upnp_thread.wait_to_finish() + _upnp_thread = null + _upnp_mapping_in_progress = false + _upnp_operation_is_renewal = false + var mapping_succeeded: bool = false + if typeof(result) == TYPE_DICTIONARY: + var details: Dictionary = result + if bool(details.get("success", false)): + _upnp = details.get("upnp") as UPNP + _upnp_mapped_port = int(details.get("port", 0)) + mapping_succeeded = true + if not _discoverable: + _begin_upnp_cleanup() + return + if mapping_succeeded: + _schedule_upnp_renewal(UPNP_RENEW_INTERVAL_SECONDS) + elif was_renewal: + _schedule_upnp_renewal(UPNP_RETRY_INTERVAL_SECONDS) + _synchronize_host_lease() + + +func _schedule_upnp_renewal(delay_seconds: float) -> void: + if ( + _upnp_mapped_port < 1 + or not _discoverable + or _session == null + or _session.is_dedicated_host() + ): + _upnp_renew_timer.stop() + return + _upnp_renew_timer.start(delay_seconds) + + +func _on_upnp_renew_timer_timeout() -> void: + if ( + not _discoverable + or _session == null + or not _session.is_open_host() + or _session.is_dedicated_host() + or _upnp_mapped_port < 1 + ): + return + _begin_upnp_mapping(_upnp_mapped_port, true) + + +func _begin_upnp_cleanup() -> void: + _upnp_renew_timer.stop() + if _upnp == null or _upnp_mapped_port < 1: + return + _upnp.delete_port_mapping(_upnp_mapped_port, "UDP") + _upnp = null + _upnp_mapped_port = 0 + + +func _on_browse_request_completed( + result: int, + response_code: int, + _headers: PackedStringArray, + body: PackedByteArray, +) -> void: + _browse_request_in_flight = false + var response: Dictionary = _parse_response_dictionary(body) + if result != HTTPRequest.RESULT_SUCCESS or response_code != HTTPClient.RESPONSE_OK: + rooms_updated.emit([]) + browse_status_changed.emit(_request_failure(response), true) + return + var raw_rooms: Variant = response.get("rooms", []) + if typeof(raw_rooms) != TYPE_ARRAY: + rooms_updated.emit([]) + browse_status_changed.emit("Discovery returned an invalid room list.", true) + return + var rooms: Array[Dictionary] = [] + for value: Variant in raw_rooms: + if typeof(value) != TYPE_DICTIONARY: + continue + var room: Dictionary = value + if _valid_public_room(room): + rooms.append(room.duplicate(true)) + rooms_updated.emit(rooms) + browse_status_changed.emit( + "No public rooms are available." + if rooms.is_empty() + else "%d public room%s found." % [rooms.size(), "" if rooms.size() == 1 else "s"], + false, + ) + + +func _valid_public_room(room: Dictionary) -> bool: + var expected_version: String = str( + ProjectSettings.get_setting("application/config/version", "unknown") + ) + return ( + typeof(room.get("room_id")) == TYPE_STRING + and typeof(room.get("room_name")) == TYPE_STRING + and typeof(room.get("address")) == TYPE_STRING + and typeof(room.get("game_version")) == TYPE_STRING + and str(room.get("game_version")) == expected_version + and _valid_json_integer(room.get("port"), 1, 65535) + and _valid_json_integer(room.get("current_players"), 0, 128) + and _valid_json_integer(room.get("max_players"), 1, 128) + and int(room["current_players"]) <= int(room["max_players"]) + and _valid_json_integer( + room.get("protocol_version"), + NetworkProtocol.PROTOCOL_VERSION, + NetworkProtocol.PROTOCOL_VERSION, + ) + ) + + +func _valid_json_integer(value: Variant, minimum: int, maximum: int) -> bool: + if typeof(value) == TYPE_INT: + return int(value) >= minimum and int(value) <= maximum + if typeof(value) != TYPE_FLOAT: + return false + var number: float = float(value) + return ( + is_finite(number) + and number == floor(number) + and number >= float(minimum) + and number <= float(maximum) + ) + + +func _parse_response_dictionary(body: PackedByteArray) -> Dictionary: + if body.is_empty(): + return {} + var parsed: Variant = JSON.parse_string(body.get_string_from_utf8()) + return parsed if typeof(parsed) == TYPE_DICTIONARY else {} + + +func _request_failure(response: Dictionary) -> String: + var error: Variant = response.get("error", {}) + if typeof(error) == TYPE_DICTIONARY: + var details := error as Dictionary + if str(details.get("code", "")) == "game_version_mismatch": + var required_version: String = str( + details.get("required_game_version", "") + ).strip_edges() + if not required_version.is_empty(): + return _discovery_version_mismatch_message(required_version) + var message: String = str(details.get("message", "")).strip_edges() + if not message.is_empty(): + return message + return "Room discovery is temporarily unavailable." + + +func _discovery_version_mismatch_message(required_version: String) -> String: + var local_version: String = NetworkProtocol.game_version() + var rejection: NetworkProtocol.RejectionCode = ( + NetworkProtocol.game_version_rejection( + local_version, + required_version, + ) + ) + match rejection: + NetworkProtocol.RejectionCode.CLIENT_OUTDATED: + return ( + "Your NETfishing version is out of date. Update to %s to " + + "enable public discovery. This room will not be listed " + + "until you update." + ) % required_version + NetworkProtocol.RejectionCode.SERVER_OUTDATED: + return ( + "Public discovery is still on NETfishing %s. This room " + + "will not be listed until discovery is updated for %s." + ) % [required_version, local_version] + _: + return ( + "Public discovery requires NETfishing %s. This room will " + + "not be listed until both versions match." + ) % required_version + + +func _on_session_state_changed(state: NetworkSession.State) -> void: + if state == NetworkSession.State.CONNECTING and not _pending_join_token.is_empty(): + _preserve_pending_join_on_inactive = false + _set_public_join_state(PublicJoinState.CONNECTING) + _join_probe_timer.start() + call_deferred("_send_pending_join_probe") + elif state in [ + NetworkSession.State.JOINED_CLIENT, + NetworkSession.State.SERVER_LOST, + ]: + _preserve_pending_join_on_inactive = false + _set_public_join_state(PublicJoinState.IDLE) + _join_probe_timer.stop() + _clear_pending_join() + elif state == NetworkSession.State.INACTIVE: + if ( + _preserve_pending_join_on_inactive + and not _pending_join_token.is_empty() + ): + _preserve_pending_join_on_inactive = false + else: + _set_public_join_state(PublicJoinState.IDLE) + _join_probe_timer.stop() + _clear_pending_join() + elif state == NetworkSession.State.CONNECTION_FAILED: + _preserve_pending_join_on_inactive = false + _set_public_join_state(PublicJoinState.ERROR) + _join_probe_timer.stop() + _clear_pending_join() + if state == NetworkSession.State.OPEN_HOST: + if _discoverable: + _heartbeat.start() + _synchronize_host_lease() + else: + _set_host_state(HostState.PRIVATE) + _set_host_status("Room is open but unlisted.", false) + return + if state in [ + NetworkSession.State.PRIVATE_HOST, + NetworkSession.State.INACTIVE, + NetworkSession.State.DISCONNECTING, + NetworkSession.State.CONNECTION_FAILED, + NetworkSession.State.SERVER_LOST, + ]: + if _discoverable: + _discoverable = false + host_settings_changed.emit(_room_name, false) + _heartbeat.stop() + _upnp_renew_timer.stop() + _remove_host_lease() + _begin_upnp_cleanup() + if state == NetworkSession.State.PRIVATE_HOST: + _set_host_state(HostState.CLOSED) + _set_host_status("Open the game before listing it publicly.", false) + else: + _set_host_state(HostState.CLOSED) + + +func _on_host_openness_changed(is_open: bool) -> void: + if not is_open and _discoverable: + set_discoverable(false) + + +func _on_peer_count_changed(_player_count: int, _max_players: int) -> void: + if _discoverable: + _synchronize_host_lease() + + +func _clear_lease() -> void: + _lease_room_id = "" + _lease_token = "" + _host_verified = false + _host_verification_token = "" + _traversal_timer.stop() + + +func _clear_pending_join() -> void: + _pending_join_endpoint = "" + _pending_join_token = "" + _pending_join_room_id = "" + _join_probe_timer.stop() + + +func _exit_tree() -> void: + _upnp_renew_timer.stop() + if _upnp_thread != null: + var result: Variant = _upnp_thread.wait_to_finish() + _upnp_thread = null + if typeof(result) == TYPE_DICTIONARY: + var details: Dictionary = result + if bool(details.get("success", false)): + _upnp = details.get("upnp") as UPNP + _upnp_mapped_port = int(details.get("port", 0)) + _begin_upnp_cleanup() + + +func _set_host_status(message: String, is_error: bool) -> void: + _host_status_message = message + _host_status_is_error = is_error + host_status_changed.emit(message, is_error) + + +func _set_host_state(state: HostState) -> void: + if _host_state == state: + return + _host_state = state + host_state_changed.emit(int(state)) + + +func _set_public_join_state(state: PublicJoinState) -> void: + if _public_join_state == state: + return + _public_join_state = state + public_join_state_changed.emit(int(state)) + + +func _sanitize_room_name(value: String) -> String: + var cleaned: String = value.strip_edges().replace("\n", " ").replace("\r", " ") + cleaned = cleaned.replace("\t", " ") + while " " in cleaned: + cleaned = cleaned.replace(" ", " ") + return cleaned.left(MAX_ROOM_NAME_LENGTH) + + +func _default_room_name(player_name: String) -> String: + var cleaned_name: String = player_name.strip_edges() + if cleaned_name.is_empty(): + return DEFAULT_ROOM_NAME + return "%s%s" % [ + cleaned_name.left(MAX_ROOM_NAME_LENGTH - ROOM_NAME_SUFFIX.length()), + ROOM_NAME_SUFFIX, + ] + + +func _load_settings() -> void: + var config := ConfigFile.new() + if config.load(SETTINGS_PATH) == OK: + var saved_name: String = _sanitize_room_name( + str(config.get_value("host", "room_name", DEFAULT_ROOM_NAME)) + ) + if not saved_name.is_empty(): + _room_name = saved_name + _room_name_uses_default = bool(config.get_value( + "host", + "room_name_uses_default", + saved_name in [ + DEFAULT_ROOM_NAME, + LEGACY_DEFAULT_ROOM_NAME, + LEGACY_DEDICATED_DEFAULT_ROOM_NAME, + ], + )) + + +func _save_settings() -> void: + if not _host_settings_persistence_enabled: + return + var config := ConfigFile.new() + config.set_value("host", "room_name", _room_name) + config.set_value( + "host", "room_name_uses_default", _room_name_uses_default + ) + var error: Error = config.save(SETTINGS_PATH) + if error != OK: + push_warning("Could not save the local NETfishing room name.") diff --git a/network/discovery_client.gd.uid b/network/discovery_client.gd.uid new file mode 100644 index 0000000..eebfd6a --- /dev/null +++ b/network/discovery_client.gd.uid @@ -0,0 +1 @@ +uid://d0jv8n2nfqia0 diff --git a/network/local_signing_identity_store.gd b/network/local_signing_identity_store.gd index 87fc4c6..a8ac7a0 100644 --- a/network/local_signing_identity_store.gd +++ b/network/local_signing_identity_store.gd @@ -9,11 +9,17 @@ var error_message: String = "" var _private_key: CryptoKey var _prefix: String = "" var _allow_generation: bool = true +var _storage_directory: String = "" -func configure(prefix: String, allow_generation: bool = true) -> void: +func configure( + prefix: String, + allow_generation: bool = true, + storage_directory: String = "", +) -> void: _prefix = prefix _allow_generation = allow_generation + _storage_directory = storage_directory.strip_edges() func load_or_create() -> bool: @@ -225,7 +231,12 @@ func _write_atomic(path: String, content: String) -> bool: func _path(extension: String) -> String: - return "user://%s%s" % [_prefix, extension] + var filename: String = "%s%s" % [_prefix, extension] + return ( + _storage_directory.path_join(filename) + if not _storage_directory.is_empty() + else "user://%s" % filename + ) func identity_type() -> String: diff --git a/network/network_chat_protocol.gd b/network/network_chat_protocol.gd index da1af63..ef7294d 100644 --- a/network/network_chat_protocol.gd +++ b/network/network_chat_protocol.gd @@ -3,8 +3,8 @@ extends RefCounted const CAPABILITY: StringName = &"chat_v1" const RELIABLE_CHANNEL: int = NetworkProtocol.CHAT_RELIABLE_CHANNEL -const MAX_VISIBLE_CHARACTERS: int = 300 -const MAX_UTF8_BYTES: int = 1200 +const MAX_VISIBLE_CHARACTERS: int = 256 +const MAX_UTF8_BYTES: int = 1024 const MAX_HISTORY: int = 100 const LATE_JOIN_HISTORY: int = 50 const MAX_ID_LENGTH: int = 96 diff --git a/network/network_chat_service.gd b/network/network_chat_service.gd index ebb65e9..50c47d3 100644 --- a/network/network_chat_service.gd +++ b/network/network_chat_service.gd @@ -4,24 +4,55 @@ extends Node const BURST_COUNT: int = 3 const WINDOW_COUNT: int = 5 const WINDOW_SECONDS: float = 10.0 +const CALL_COOLDOWN_MILLISECONDS: int = 180 +const WORLD_COMMAND_COOLDOWN_MILLISECONDS: int = 500 +const CALL_PITCH_VARIANTS: Array[float] = [ + 0.96, + 1.03, + 0.985, + 1.055, + 1.0, + 0.975, + 1.04, +] +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) signal message_received(message: Dictionary) signal local_message_confirmed(message: Dictionary) signal send_rejected(message: String) signal history_replaced(messages: Array[Dictionary]) +signal world_command_finished(success: bool, message: String) +signal character_call_received( + peer_id: int, + call_id: String, + pitch_scale: float, +) var _session: NetworkSession var _history: Array[Dictionary] = [] var _seen_messages: Dictionary[String, bool] = {} var _request_ledgers: Dictionary[int, Dictionary] = {} var _rate_times: Dictionary[int, Array] = {} +var _last_call_msec: Dictionary[int, int] = {} +var _call_variant_indices: Dictionary[int, int] = {} +var _last_world_command_msec: Dictionary[int, int] = {} var _sequence: int = 0 var _peer_names: Dictionary[int, String] = {} var _relationships: PlayerRelationshipStore +var _world_time: WorldTimeService +var _world_weather: WorldWeatherService -func setup(session: NetworkSession) -> void: +func setup( + session: NetworkSession, + world_time: WorldTimeService, + world_weather: WorldWeatherService, +) -> void: _session = session + _world_time = world_time + _world_weather = world_weather _session.peer_authenticated.connect(_on_peer_authenticated) _session.peer_removed.connect(_on_peer_removed) _session.state_changed.connect(_on_session_state_changed) @@ -79,6 +110,331 @@ func send_local_message(body: String) -> bool: return true +func send_local_character_call(call_id: String) -> bool: + if ( + _session == null + or not _session.is_gameplay_session_active() + or not VoiceProfilesType.is_valid_call(call_id) + or ( + not _session.is_host() + and not _session.supports_server_capability( + NetworkChatProtocol.CAPABILITY + ) + ) + ): + return false + var request := { + "request_id": _new_id("character_call"), + "session_id": _session.get_session_id(), + "call_id": call_id, + "sender_fingerprint": _session.get_local_identity_fingerprint(), + } + request["sender_signature"] = _session.sign_local_action( + "character_call", _character_call_signature_fields(request) + ) + if _session.is_host(): + _handle_character_call(_session.get_local_peer_id(), request) + else: + submit_character_call.rpc_id(1, request) + return true + + +@rpc("any_peer", "call_remote", "reliable", NetworkChatProtocol.RELIABLE_CHANNEL) +func submit_character_call(data: Dictionary) -> void: + var sender_id := multiplayer.get_remote_sender_id() + if _session.is_host() and _session.is_authenticated_peer(sender_id): + _handle_character_call(sender_id, data) + + +func _handle_character_call(peer_id: int, data: Dictionary) -> void: + if ( + typeof(data.get("request_id")) != TYPE_STRING + or str(data["request_id"]).is_empty() + or str(data["request_id"]).length() > 64 + or typeof(data.get("session_id")) != TYPE_STRING + or str(data["session_id"]) != _session.get_session_id() + or typeof(data.get("call_id")) != TYPE_STRING + or not VoiceProfilesType.is_valid_call(str(data["call_id"])) + or typeof(data.get("sender_fingerprint")) != TYPE_STRING + or typeof(data.get("sender_signature")) != TYPE_PACKED_BYTE_ARRAY + ): + return + var record := _session.get_peer_record(peer_id) + if ( + record == null + or record.identity_fingerprint != str(data["sender_fingerprint"]) + or not _session.verify_peer_action( + peer_id, + "character_call", + _character_call_signature_fields(data), + data["sender_signature"], + ) + or not _consume_character_call_rate(peer_id) + ): + return + var pitch_scale: float = _next_character_call_pitch(peer_id) + _apply_character_call(peer_id, str(data["call_id"]), pitch_scale) + receive_character_call.rpc( + peer_id, + str(data["call_id"]), + pitch_scale, + ) + + +@rpc("authority", "call_remote", "reliable", NetworkChatProtocol.RELIABLE_CHANNEL) +func receive_character_call( + peer_id: int, + call_id: String, + pitch_scale: float, +) -> void: + _apply_character_call(peer_id, call_id, pitch_scale) + + +func _apply_character_call( + peer_id: int, + call_id: String, + pitch_scale: float, +) -> void: + if ( + not VoiceProfilesType.is_valid_call(call_id) + or pitch_scale < 0.9 + or pitch_scale > 1.1 + or _session == null + or not _session.is_gameplay_session_active() + ): + return + var record := _session.get_peer_record(peer_id) + if record == null or is_sender_filtered(record.identity_fingerprint): + return + character_call_received.emit(peer_id, call_id, pitch_scale) + + +func _next_character_call_pitch(peer_id: int) -> float: + var variant_index: int = _call_variant_indices.get(peer_id, 0) + var pitch_scale: float = CALL_PITCH_VARIANTS[ + variant_index % CALL_PITCH_VARIANTS.size() + ] + _call_variant_indices[peer_id] = variant_index + 1 + return pitch_scale + + +func _consume_character_call_rate(peer_id: int) -> bool: + var now_msec: int = Time.get_ticks_msec() + var last_msec: int = _last_call_msec.get( + peer_id, now_msec - CALL_COOLDOWN_MILLISECONDS + ) + if now_msec - last_msec < CALL_COOLDOWN_MILLISECONDS: + return false + _last_call_msec[peer_id] = now_msec + return true + + +func _character_call_signature_fields(data: Dictionary) -> Array: + return [ + str(data.get("session_id", "")), + str(data.get("request_id", "")), + str(data.get("sender_fingerprint", "")), + str(data.get("call_id", "")), + ] + + +func broadcast_system_message(body: String) -> bool: + if _session == null or not _session.is_host(): + return false + var clean := NetworkChatProtocol.sanitize_body(body) + if clean.is_empty(): + return false + _broadcast(_make_message( + NetworkChatProtocol.Kind.SYSTEM, + 0, + "", + clean, + )) + return true + + +func request_world_time_change(phase_name: String) -> bool: + var normalized: String = phase_name.strip_edges().to_lower() + if not _valid_time_phase(normalized) or _session == null: + return false + if _session.is_host(): + return _apply_world_time_change(normalized) + if ( + not _session.is_joined_client() + or not _session.is_local_operator() + or not _session.supports_server_capability( + NetworkProtocol.WORLD_TIME_CAPABILITY + ) + ): + return false + submit_world_time_command.rpc_id(1, normalized) + return true + + +func request_world_weather_change(weather_name: String) -> bool: + var normalized: String = _normalized_weather_name(weather_name) + if normalized.is_empty() or _session == null: + return false + if _session.is_host(): + return _apply_world_weather_change(normalized) + if ( + not _session.is_joined_client() + or not _session.is_local_operator() + or not _session.supports_server_capability( + NetworkProtocol.WORLD_WEATHER_CAPABILITY + ) + ): + return false + submit_world_weather_command.rpc_id(1, normalized) + return true + + +@rpc("any_peer", "call_remote", "reliable", NetworkChatProtocol.RELIABLE_CHANNEL) +func submit_world_time_command(phase_name: String) -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if not _valid_world_command_sender(sender_id): + _send_world_command_result( + sender_id, false, "Only the host or an operator can change world time." + ) + return + if not _consume_world_command_rate(sender_id): + _send_world_command_result(sender_id, false, "Slow down.") + return + var success: bool = _apply_world_time_change( + phase_name.strip_edges().to_lower() + ) + _send_world_command_result( + sender_id, + success, + "" if success else "World time could not be changed.", + ) + + +@rpc("any_peer", "call_remote", "reliable", NetworkChatProtocol.RELIABLE_CHANNEL) +func submit_world_weather_command(weather_name: String) -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if not _valid_world_command_sender(sender_id): + _send_world_command_result( + sender_id, + false, + "Only the host or an operator can change world weather.", + ) + return + if not _consume_world_command_rate(sender_id): + _send_world_command_result(sender_id, false, "Slow down.") + return + var success: bool = _apply_world_weather_change( + _normalized_weather_name(weather_name) + ) + _send_world_command_result( + sender_id, + success, + "" if success else "World weather could not be changed.", + ) + + +@rpc("authority", "call_remote", "reliable", NetworkChatProtocol.RELIABLE_CHANNEL) +func receive_world_command_result(success: bool, message: String) -> void: + if _session == null or not _session.is_joined_client(): + return + world_command_finished.emit(success, message.left(120)) + + +func _valid_world_command_sender(peer_id: int) -> bool: + return ( + _session != null + and _session.is_host() + and peer_id > 1 + and _session.is_authenticated_peer(peer_id) + and _session.is_peer_operator(peer_id) + ) + + +func _apply_world_time_change(phase_name: String) -> bool: + if _world_time == null or not _valid_time_phase(phase_name): + return false + var target_hour: float = _time_phase_hour(phase_name) + if not _world_time.set_authoritative_time(target_hour): + return false + broadcast_system_message( + "World time set to %s (%s)." + % [phase_name, _world_time.get_clock_text()] + ) + return true + + +func _apply_world_weather_change(weather_name: String) -> bool: + if _world_weather == null: + return false + var normalized: String = _normalized_weather_name(weather_name) + if normalized.is_empty(): + return false + var target_weather: WorldWeatherService.Weather = ( + _weather_for_name(normalized) + ) + if not _world_weather.set_authoritative_weather(target_weather): + return false + broadcast_system_message("World weather set to %s." % normalized) + return true + + +func _send_world_command_result( + peer_id: int, + success: bool, + message: String, +) -> void: + if peer_id > 1 and _session.is_authenticated_peer(peer_id): + receive_world_command_result.rpc_id(peer_id, success, message.left(120)) + + +func _consume_world_command_rate(peer_id: int) -> bool: + var now_msec: int = Time.get_ticks_msec() + var last_msec: int = _last_world_command_msec.get( + peer_id, now_msec - WORLD_COMMAND_COOLDOWN_MILLISECONDS + ) + if now_msec - last_msec < WORLD_COMMAND_COOLDOWN_MILLISECONDS: + return false + _last_world_command_msec[peer_id] = now_msec + return true + + +static func _valid_time_phase(phase_name: String) -> bool: + return phase_name in ["dawn", "day", "dusk", "night"] + + +static func _time_phase_hour(phase_name: String) -> float: + match phase_name: + "dawn": + return WorldTimeService.DAWN_START_HOUR + "day": + return WorldTimeService.DAWN_END_HOUR + "dusk": + return WorldTimeService.DUSK_START_HOUR + "night": + return WorldTimeService.DUSK_END_HOUR + return -1.0 + + +static func _normalized_weather_name(weather_name: String) -> String: + var normalized: String = weather_name.strip_edges().to_lower() + return "clear" if normalized == "sunny" else normalized if normalized in [ + "clear", "cloudy", "rainy", "foggy" + ] else "" + + +static func _weather_for_name( + weather_name: String, +) -> WorldWeatherService.Weather: + match weather_name: + "cloudy": + return WorldWeatherService.Weather.CLOUDY + "rainy": + return WorldWeatherService.Weather.RAINY + "foggy": + return WorldWeatherService.Weather.FOGGY + return WorldWeatherService.Weather.SUNNY + + func get_history() -> Array[Dictionary]: var result: Array[Dictionary] = [] for message: Dictionary in _history: @@ -257,6 +613,9 @@ func _on_peer_authenticated(peer_id: int, display_name: String) -> void: func _on_peer_removed(peer_id: int) -> void: _request_ledgers.erase(peer_id) _rate_times.erase(peer_id) + _last_call_msec.erase(peer_id) + _call_variant_indices.erase(peer_id) + _last_world_command_msec.erase(peer_id) if not _session.is_host(): return var display_name: String = _peer_names.get(peer_id, "Player") @@ -314,6 +673,9 @@ func _on_session_state_changed(state: NetworkSession.State) -> void: _seen_messages.clear() _request_ledgers.clear() _rate_times.clear() + _last_call_msec.clear() + _call_variant_indices.clear() + _last_world_command_msec.clear() _peer_names.clear() _sequence = 0 history_replaced.emit([]) diff --git a/network/network_fish_showcase_service.gd b/network/network_fish_showcase_service.gd index 822d2e8..068ee7d 100644 --- a/network/network_fish_showcase_service.gd +++ b/network/network_fish_showcase_service.gd @@ -39,6 +39,19 @@ func setup( _local_hotbar.selected_assignment_changed.connect( _on_selected_assignment_changed ) + var selected_kind: int = ( + _local_hotbar.get_selected_assignment_kind() + ) + var selected_identity: StringName = StringName() + if selected_kind == PlayerHotbarType.AssignmentKind.ITEM: + selected_identity = _local_hotbar.get_selected_item_id() + elif selected_kind == PlayerHotbarType.AssignmentKind.FISH: + selected_identity = _local_hotbar.get_selected_fish_catch_id() + _on_selected_assignment_changed( + _local_hotbar.get_selected_slot(), + selected_kind, + selected_identity, + ) func toggle_selected_fish() -> bool: @@ -168,14 +181,9 @@ func _state_matches_catalog(data: Dictionary) -> bool: if fish == null or not fish.is_selectable(): return false var weight_lb: float = float(data["weight_lb"]) - var display_scale: float = float(data["display_scale"]) return ( weight_lb >= fish.get_minimum_weight() and weight_lb <= fish.get_maximum_weight() - and is_equal_approx( - display_scale, - fish.get_display_scale_for_weight(weight_lb), - ) ) @@ -185,12 +193,16 @@ func _apply_state(data: Dictionary) -> void: if avatar == null: return if not bool(data["visible"]): - avatar.set_held_fish(null, 1.0, false) + avatar.set_held_fish(null, 1.0, false, true) return var fish: FishDataType = _fish_catalog.get_fish_by_id( StringName(str(data["fish_id"])) ) - avatar.set_held_fish(fish, float(data["display_scale"]), true) + avatar.set_held_fish( + fish, + fish.get_display_scale_for_weight(float(data["weight_lb"])), + true, + ) func _on_selected_assignment_changed( @@ -198,13 +210,16 @@ func _on_selected_assignment_changed( kind: int, identity: StringName, ) -> void: - if ( - _local_visible - and ( - kind != PlayerHotbarType.AssignmentKind.FISH - or identity != _local_catch_id - ) - ): + if kind == PlayerHotbarType.AssignmentKind.FISH: + if _local_visible and identity == _local_catch_id: + return + var fish_catch: FishCatchType = _local_inventory.get_catch_by_id(identity) + if fish_catch != null and fish_catch.is_valid(): + _submit_local_state(fish_catch, true) + elif _local_visible: + _submit_local_state(null, false) + return + if _local_visible: _submit_local_state(null, false) diff --git a/network/network_fishing_attempt.gd b/network/network_fishing_attempt.gd index c48bab5..3a1b1de 100644 --- a/network/network_fishing_attempt.gd +++ b/network/network_fishing_attempt.gd @@ -21,8 +21,11 @@ var origin: Vector3 var target: Vector3 var bobber_position: Vector3 var fish_id: StringName +var bait_tags: Array[StringName] = [] +var lure_effects: Array[StringName] = [] var encounter_seed: int = 0 var bite_time_remaining: float = 0.0 +var bite_confirmation_pending: bool = false var withdrawal_progress: float = 0.0 var reel_speed: float = 0.0 var barrier_damage: int = 1 diff --git a/network/network_fishing_protocol.gd b/network/network_fishing_protocol.gd index e5e6720..2e9a342 100644 --- a/network/network_fishing_protocol.gd +++ b/network/network_fishing_protocol.gd @@ -53,6 +53,10 @@ static func validate_cast_request(data: Variant) -> String: or typeof(payload["capacity_available"]) != TYPE_BOOL ): return "Malformed fishing request." + if payload.has("bait_id") and typeof(payload["bait_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]: + return "Malformed fishing request." + if payload.has("lure_id") and typeof(payload["lure_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]: + return "Malformed fishing request." var request_id: String = payload["request_id"] var session_id: String = payload["session_id"] var origin: Array = payload["origin"] @@ -87,6 +91,7 @@ static func validate_cast_request(data: Variant) -> String: or discovered.size() > MAX_EVIDENCE_FISH_IDS or str(payload["rod_id"]).is_empty() or str(payload["rod_id"]).length() > 96 + or str(payload.get("lure_id", "")).length() > 96 ): return "Fishing request values are outside allowed limits." for value: Variant in rarity: diff --git a/network/network_fishing_service.gd b/network/network_fishing_service.gd index be214e2..2015681 100644 --- a/network/network_fishing_service.gd +++ b/network/network_fishing_service.gd @@ -14,6 +14,7 @@ const PlayerExperienceType = preload( const RemotePresentationType = preload( "res://fishing/remote_fishing_presentation.gd" ) +const ItemDataType = preload("res://items/item_data.gd") const MAX_LEDGER_ENTRIES_PER_PEER: int = 64 const CAST_ORIGIN_TOLERANCE: float = 2.5 @@ -23,6 +24,7 @@ const MIN_CAST_INTERVAL: float = 0.25 signal local_cast_accepted(attempt_id: String, target: Vector3) signal local_cast_rejected(message: String) signal local_bite_started(attempt_id: String) +signal local_bite_pending(attempt_id: String) signal local_snapshot_received(snapshot: Dictionary) signal local_catch_received(fish_catch: FishCatch) signal local_attempt_ended(outcome: StringName, message: String) @@ -31,6 +33,7 @@ var _session: NetworkSession var _spawn_service: PlayerSpawnService var _fishing_spot: FishingSpot var _local_inventory: FishInventory +var _local_bag: PlayerBag var _local_collection: CollectionLog var _local_capacity: PlayerCoolerCapacity var _local_experience: PlayerExperienceType @@ -45,6 +48,7 @@ var _result_acknowledgements: Dictionary[String, String] = {} var _last_cast_time: Dictionary[int, float] = {} var _last_input_time: Dictionary[int, float] = {} var _remote_presentations: Dictionary[int, RemoteFishingPresentation] = {} +var _pending_local_bait_by_request: Dictionary[String, StringName] = {} var _snapshot_accumulator: float = 0.0 var _local_input_sequence: int = 0 @@ -54,6 +58,7 @@ func setup( spawn_service: PlayerSpawnService, fishing_spot: FishingSpot, local_inventory: FishInventory, + local_bag: PlayerBag, local_collection: CollectionLog, local_capacity: PlayerCoolerCapacity, local_experience: PlayerExperienceType, @@ -66,6 +71,7 @@ func setup( _spawn_service = spawn_service _fishing_spot = fishing_spot _local_inventory = local_inventory + _local_bag = local_bag _local_collection = local_collection _local_capacity = local_capacity _local_experience = local_experience @@ -95,6 +101,18 @@ func request_local_cast( ): local_cast_rejected.emit("Fishing attempt ended.") return "" + var bait_id := StringName(str(evidence.get("bait_id", ""))) + if not bait_id.is_empty() and ( + _local_bag == null or not _local_bag.owns_item(bait_id) + ): + local_cast_rejected.emit("No bait available.") + return "" + var lure_id := StringName(str(evidence.get("lure_id", ""))) + if not lure_id.is_empty() and ( + _local_bag == null or not _local_bag.owns_item(lure_id) + ): + local_cast_rejected.emit("Lure is unavailable.") + return "" var request_id: String = _new_id("cast") var data: Dictionary = { "request_id": request_id, @@ -109,10 +127,14 @@ func request_local_cast( "rarity_multipliers": evidence.get("rarity_multipliers", []), "discovered_fish_ids": evidence.get("discovered_fish_ids", []), "capacity_available": bool(evidence.get("capacity_available", false)), + "bait_id": str(bait_id), + "lure_id": str(lure_id), } if _session.is_host(): _handle_cast_request(_session.get_local_peer_id(), data) else: + if not bait_id.is_empty(): + _pending_local_bait_by_request[request_id] = bait_id submit_cast_request.rpc_id(1, data) return request_id @@ -174,12 +196,17 @@ func _process(delta: float) -> void: continue match attempt.phase: NetworkFishingAttempt.Phase.WAITING_FOR_BITE: + if attempt.bite_confirmation_pending: + continue _update_waiting_attempt(attempt, delta) if not _attempts.has(peer_id): continue attempt.bite_time_remaining -= delta if attempt.bite_time_remaining <= 0.0: - _start_bite(attempt) + if &"deferred_fight" in attempt.lure_effects: + _set_bite_pending(attempt) + else: + _start_bite(attempt) NetworkFishingAttempt.Phase.PENDING_CAPACITY: if now >= attempt.capacity_deadline: _cancel_attempt(peer_id, "Fishing attempt ended.") @@ -292,6 +319,51 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void: ): _record_and_reject(peer_id, request_id, "Nothing is biting here.") return + var bait_id: StringName = StringName(str(data.get("bait_id", ""))) + var avatar_bag: PlayerBag = avatar.bag + var owns_authoritative_bag: bool = ( + peer_id == _session.get_local_peer_id() + ) + var bait: ItemDataType = ( + _item_catalog.get_item_by_id(bait_id) + if not bait_id.is_empty() and _item_catalog != null + else null + ) + var lure_id: StringName = StringName(str(data.get("lure_id", ""))) + var lure: ItemDataType = ( + _item_catalog.get_item_by_id(lure_id) + if not lure_id.is_empty() and _item_catalog != null + else null + ) + if ( + not lure_id.is_empty() + and ( + lure == null + or not lure.is_lure() + or ( + owns_authoritative_bag + and ( + avatar_bag == null + or not avatar_bag.owns_item(lure_id) + ) + ) + ) + ): + _record_and_reject(peer_id, request_id, "Lure is unavailable.") + return + if not bait_id.is_empty() and ( + bait == null + or not bait.is_bait() + or ( + owns_authoritative_bag + and ( + avatar_bag == null + or not avatar_bag.remove_item(bait_id, 1) + ) + ) + ): + _record_and_reject(peer_id, request_id, "No bait available.") + return var attempt := NetworkFishingAttempt.new() attempt.owner_peer_id = peer_id attempt.request_id = request_id @@ -302,6 +374,11 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void: attempt.target = authoritative_target attempt.bobber_position = authoritative_target attempt.fish_id = selected_fish.id + attempt.bait_tags = _bait_tags_for_request(data) + attempt.lure_effects.clear() + if lure != null: + for effect_id: StringName in lure.lure_effects: + attempt.lure_effects.append(effect_id) attempt.reel_speed = float(data["reel_speed"]) * ( effects.get_reel_multiplier() if effects != null else 1.0 ) @@ -413,6 +490,7 @@ func _select_authoritative_fish( ) selector.begin_roll() var context: FishingContextType = _fishing_spot.build_network_context(region) + context.active_bait_tags = _bait_tags_for_request(data) var selected_fish := selector.select_fish( region.fish_pool, context, evidence_log ) @@ -420,6 +498,31 @@ func _select_authoritative_fish( return selected_fish +func _bait_tags_for_request(data: Dictionary) -> Array[StringName]: + var bait_id: StringName = StringName(str(data.get("bait_id", ""))) + if bait_id.is_empty() or _item_catalog == null: + return [] + var bait: ItemDataType = _item_catalog.get_item_by_id(bait_id) + return bait.bait_tags.duplicate() if bait != null and bait.is_bait() else [] + + +func _set_bite_pending(attempt: NetworkFishingAttempt) -> void: + if ( + attempt.phase != NetworkFishingAttempt.Phase.WAITING_FOR_BITE + or attempt.bite_confirmation_pending + ): + return + attempt.bite_confirmation_pending = true + attempt.bite_time_remaining = 0.0 + attempt.input_held = false + var data: Dictionary = { + "attempt_id": attempt.attempt_id, + "owner_peer_id": attempt.owner_peer_id, + } + _apply_bite_pending(data) + receive_bite_pending.rpc(data) + + func _start_bite(attempt: NetworkFishingAttempt) -> void: if attempt.phase != NetworkFishingAttempt.Phase.WAITING_FOR_BITE: return @@ -427,13 +530,24 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void: if fish == null or fish.catch_profile == null: _cancel_attempt(attempt.owner_peer_id, "Fishing attempt ended.") return + attempt.bite_confirmation_pending = false attempt.phase = NetworkFishingAttempt.Phase.FIGHTING attempt.encounter_seed = _new_seed() + var selector := FishSelectorType.new() + selector.use_deterministic_test_seed = true + selector.deterministic_test_seed = attempt.encounter_seed ^ 0x5F3759DF + selector.begin_roll() + var fish_catch: FishCatch = selector.create_catch(fish, attempt.bait_tags) + if fish_catch == null or not fish_catch.is_valid(): + _cancel_attempt(attempt.owner_peer_id, "Fishing attempt ended.") + return + attempt.catch_payload = fish_catch.to_network_dict() attempt.controller.start_authoritative_encounter( fish.catch_profile, attempt.reel_speed, attempt.barrier_damage, - attempt.encounter_seed + attempt.encounter_seed, + fish_catch.quality, ) var data: Dictionary = { "attempt_id": attempt.attempt_id, @@ -467,6 +581,14 @@ func _handle_fishing_input(peer_id: int, data: Dictionary) -> void: return attempt.last_input_sequence = int(data["sequence"]) attempt.input_held = bool(data["held"]) + if ( + attempt.phase == NetworkFishingAttempt.Phase.WAITING_FOR_BITE + and attempt.bite_confirmation_pending + ): + if bool(data["pressed"]): + attempt.input_held = false + _start_bite(attempt) + return if attempt.phase != NetworkFishingAttempt.Phase.FIGHTING: return attempt.controller.set_reel_input(attempt.input_held) @@ -617,11 +739,10 @@ func _on_attempt_caught(peer_id: int) -> void: if fish == null: _cancel_attempt(peer_id, "Fishing attempt ended.") return - var selector := FishSelectorType.new() - selector.use_deterministic_test_seed = true - selector.deterministic_test_seed = attempt.encounter_seed ^ 0x5F3759DF - selector.begin_roll() - var fish_catch: FishCatch = selector.create_catch(fish) + var fish_catch: FishCatch = FishCatchType.from_network_dict( + attempt.catch_payload, + fish, + ) if fish_catch == null or not fish_catch.is_valid(): _cancel_attempt(peer_id, "Fishing attempt ended.") return @@ -832,7 +953,7 @@ func _on_attempt_escaped(peer_id: int) -> void: if attempt == null: return attempt.phase = NetworkFishingAttempt.Phase.ESCAPED - _broadcast_public_outcome(attempt, &"escape", "The fish got away!") + _broadcast_public_outcome(attempt, &"escape", "") _dispose_attempt(peer_id) @@ -943,6 +1064,20 @@ func _apply_cast_accepted(data: Dictionary) -> void: # On the host this replaces the same authoritative value with itself. if not _session.is_host(): _attempts[peer_id] = attempt + var pending_bait_id: StringName = ( + _pending_local_bait_by_request.get( + attempt.request_id, + StringName(), + ) + ) + _pending_local_bait_by_request.erase(attempt.request_id) + if not pending_bait_id.is_empty() and ( + _local_bag == null + or not _local_bag.remove_item(pending_bait_id, 1) + ): + cancel_local_attempt("No bait available.") + local_cast_rejected.emit("No bait available.") + return local_cast_accepted.emit(attempt.attempt_id, target) else: var presentation := _get_remote_presentation(peer_id) @@ -950,6 +1085,21 @@ func _apply_cast_accepted(data: Dictionary) -> void: presentation.show_cast(origin, target) +@rpc("authority", "call_remote", "reliable", 0) +func receive_bite_pending(data: Dictionary) -> void: + _apply_bite_pending(data) + + +func _apply_bite_pending(data: Dictionary) -> void: + if ( + typeof(data.get("attempt_id")) != TYPE_STRING + or typeof(data.get("owner_peer_id")) != TYPE_INT + ): + return + if int(data["owner_peer_id"]) == _session.get_local_peer_id(): + local_bite_pending.emit(str(data["attempt_id"])) + + @rpc("authority", "call_remote", "reliable", 0) func receive_bite_started(data: Dictionary) -> void: _apply_bite_started(data) @@ -996,7 +1146,8 @@ func _send_cast_rejected( @rpc("authority", "call_remote", "reliable", 0) -func receive_cast_rejected(_request_id: String, message: String) -> void: +func receive_cast_rejected(request_id: String, message: String) -> void: + _pending_local_bait_by_request.erase(request_id) local_cast_rejected.emit(message) diff --git a/network/network_item_use_service.gd b/network/network_item_use_service.gd index 654b45b..f39d198 100644 --- a/network/network_item_use_service.gd +++ b/network/network_item_use_service.gd @@ -50,9 +50,19 @@ func request_use(item_id: StringName) -> String: if not _pending_local.is_empty(): local_item_use_finished.emit(false, "An item use is already pending.") return "" + var consumed_item_id: StringName = _consumed_item_id(item_id) + if item_id == PlayerItemEffects.FISH_FINDER_ID and ( + _local_bag == null + or not _local_bag.owns_item(item_id) + or _local_bag.get_quantity(consumed_item_id) < 1 + ): + local_item_use_finished.emit( + false, PlayerItemEffects.FISH_FINDER_DEAD_MESSAGE + ) + return "" if ( _reservations != null - and _reservations.get_available_item_quantity(item_id) < 1 + and _reservations.get_available_item_quantity(consumed_item_id) < 1 ): local_item_use_finished.emit(false, "Reserved in a letter.") return "" @@ -73,7 +83,7 @@ func request_use(item_id: StringName) -> String: "request_id": request_id, "session_id": _session.get_session_id(), "item_id": str(item_id), - "quantity": _local_bag.get_quantity(item_id), + "quantity": _local_bag.get_quantity(consumed_item_id), } _pending_local = data.duplicate(true) local_item_use_pending.emit(request_id) @@ -109,14 +119,28 @@ func _handle_use_request(peer_id: int, data: Dictionary) -> void: var item: ItemData = _catalog.get_item_by_id(item_id) var avatar: Player = _spawn_service.get_avatar(peer_id) var duration: float = 0.0 + var is_fish_finder: bool = ( + item_id == PlayerItemEffects.FISH_FINDER_ID + and item != null + and item.category == ItemData.Category.TOOL + and item.usable + and item.equippable + ) if error.is_empty() and ( item == null - or item.category != ItemData.Category.CONSUMABLE + or ( + item.category != ItemData.Category.CONSUMABLE + and not is_fish_finder + ) or not item.usable ): error = "That item cannot be used now." if error.is_empty() and int(data["quantity"]) < 1: - error = "You do not have that item." + error = ( + PlayerItemEffects.FISH_FINDER_DEAD_MESSAGE + if is_fish_finder + else "You do not have that item." + ) if error.is_empty() and ( avatar == null or avatar.is_water_recovery_active() @@ -180,9 +204,10 @@ func _apply_result(data: Dictionary) -> void: _acknowledge(data, false, str(data["message"])) return var item_id := StringName(str(data["item_id"])) + var consumed_item_id: StringName = _consumed_item_id(item_id) if ( _reservations != null - and _reservations.get_available_item_quantity(item_id) < 1 + and _reservations.get_available_item_quantity(consumed_item_id) < 1 ): _pending_local.clear() local_item_use_finished.emit(false, "Reserved in a letter.") @@ -190,8 +215,8 @@ func _apply_result(data: Dictionary) -> void: return var bag_snapshot := _local_bag.get_all_items() if ( - _local_bag.get_quantity(item_id) < 1 - or not _local_bag.remove_item(item_id, 1) + _local_bag.get_quantity(consumed_item_id) < 1 + or not _local_bag.remove_item(consumed_item_id, 1) or not _save_manager.save_if_dirty() ): _local_bag.replace_all_items(bag_snapshot) @@ -328,7 +353,10 @@ func _apply_equipped(data: Dictionary) -> void: if avatar != null: var item_id := StringName(str(data["item_id"])) var item: ItemData = _catalog.get_item_by_id(item_id) - avatar.set_active_item_is_rod(int(data["category"]) == ItemData.Category.ROD) + avatar.set_active_item_is_rod( + int(data["category"]) == ItemData.Category.ROD, + true, + ) avatar.set_active_art_kit( item.icon if item != null else null, item_id == ArtShopStockType.ART_KIT_ITEM_ID and bool(data["owns_item"]), @@ -409,3 +437,11 @@ func _new_id(prefix: String) -> String: return "%s:%s" % [ prefix, Crypto.new().generate_random_bytes(16).hex_encode(), ] + + +func _consumed_item_id(item_id: StringName) -> StringName: + return ( + PlayerItemEffects.BATTERIES_ID + if item_id == PlayerItemEffects.FISH_FINDER_ID + else item_id + ) diff --git a/network/network_job_service.gd b/network/network_job_service.gd new file mode 100644 index 0000000..ebf5a6e --- /dev/null +++ b/network/network_job_service.gd @@ -0,0 +1,155 @@ +class_name NetworkJobService +extends Node + +const MAX_SESSION_ID_LENGTH: int = 96 +const BOARD_REQUEST_INTERVAL_SECONDS: float = 1.5 + +var _session: NetworkSession +var _jobs: PlayerJobService +var _sequence: int = 0 +var _last_received_sequence: int = -1 +var _board_request_accumulator: float = 0.0 + + +func setup(session: NetworkSession, jobs: PlayerJobService) -> void: + _session = session + _jobs = jobs + _session.state_changed.connect(_on_session_state_changed) + _session.peer_authenticated.connect(_on_peer_authenticated) + _jobs.board_changed.connect(_on_board_changed) + set_process(true) + + +func _process(delta: float) -> void: + if ( + _session == null + or _jobs == null + or not _session.is_joined_client() + or not _session.supports_server_capability( + NetworkProtocol.JOBS_CAPABILITY + ) + or _jobs.has_active_board() + ): + _board_request_accumulator = 0.0 + return + _board_request_accumulator += delta + if _board_request_accumulator < BOARD_REQUEST_INTERVAL_SECONDS: + return + _board_request_accumulator = 0.0 + _request_remote_board() + + +func _on_session_state_changed(state: NetworkSession.State) -> void: + _sequence = 0 + _last_received_sequence = -1 + _board_request_accumulator = 0.0 + if state == NetworkSession.State.JOINED_CLIENT: + if _session.supports_server_capability(NetworkProtocol.JOBS_CAPABILITY): + _request_remote_board() + else: + _jobs.clear_remote_board() + elif state in [ + NetworkSession.State.INACTIVE, + NetworkSession.State.DISCONNECTING, + NetworkSession.State.CONNECTION_FAILED, + NetworkSession.State.SERVER_LOST, + ]: + _jobs.clear_remote_board() + + +func _on_peer_authenticated(peer_id: int, _display_name: String) -> void: + if ( + _session == null + or not _session.is_host() + or not _session.peer_supports_capability( + peer_id, NetworkProtocol.JOBS_CAPABILITY + ) + ): + return + _send_board(peer_id) + + +func _on_board_changed() -> void: + if _session == null or not _session.is_host(): + return + for peer_id: int in _session.get_authenticated_peer_ids(): + if ( + peer_id != _session.get_local_peer_id() + and _session.peer_supports_capability( + peer_id, NetworkProtocol.JOBS_CAPABILITY + ) + ): + _send_board(peer_id) + + +func _send_board(peer_id: int) -> void: + var board: Dictionary = _jobs.get_host_board_network_data() + if board.is_empty() or not PlayerJobService.validate_board(board): + return + _sequence += 1 + receive_job_board.rpc_id(peer_id, { + "session_id": _session.get_session_id(), + "sequence": _sequence, + "board": board, + }) + + +func _request_remote_board() -> void: + if ( + _session != null + and _session.is_joined_client() + and _session.supports_server_capability( + NetworkProtocol.JOBS_CAPABILITY + ) + ): + request_job_board.rpc_id(1) + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_job_board() -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if ( + _session == null + or not _session.is_host() + or sender_id <= 1 + or not _session.is_authenticated_peer(sender_id) + or not _session.peer_supports_capability( + sender_id, NetworkProtocol.JOBS_CAPABILITY + ) + ): + return + _send_board(sender_id) + + +@rpc("authority", "call_remote", "reliable", 0) +func receive_job_board(data: Dictionary) -> void: + if ( + _session == null + or _jobs == null + or not _session.is_joined_client() + or not _session.supports_server_capability(NetworkProtocol.JOBS_CAPABILITY) + or not validate_snapshot(data) + or str(data.get("session_id", "")) != _session.get_session_id() + ): + return + var sequence: int = int(data.get("sequence", -1)) + if sequence <= _last_received_sequence: + return + var board: Dictionary = data.get("board", {}) + if _jobs.apply_remote_board(board): + _last_received_sequence = sequence + _board_request_accumulator = 0.0 + + +static func validate_snapshot(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var data: Dictionary = value + return ( + typeof(data.get("session_id")) == TYPE_STRING + and not str(data.get("session_id", "")).is_empty() + and str(data.get("session_id", "")).length() <= MAX_SESSION_ID_LENGTH + and typeof(data.get("sequence")) == TYPE_INT + and int(data.get("sequence", -1)) >= 0 + and PlayerJobService.validate_board(data.get("board")) + ) diff --git a/network/network_job_service.gd.uid b/network/network_job_service.gd.uid new file mode 100644 index 0000000..d50e8dc --- /dev/null +++ b/network/network_job_service.gd.uid @@ -0,0 +1 @@ +uid://drlyytr2o3uw8 diff --git a/network/network_mail_service.gd b/network/network_mail_service.gd index f63c7ce..eb9fe0f 100644 --- a/network/network_mail_service.gd +++ b/network/network_mail_service.gd @@ -992,6 +992,7 @@ func _capture_assets() -> Dictionary: return { "wallet": _wallet.get_balance(), "bag": _bag.get_all_items(), + "unlocked_bait_ids": _bag.get_unlocked_bait_ids(), "catches": _inventory.get_all_catches(), "next_sequence": _inventory.get_next_catch_sequence(), "discovered": _collection_log.get_discovered_ids(), @@ -1002,6 +1003,7 @@ func _capture_assets() -> Dictionary: func _restore_assets(snapshot: Dictionary) -> void: _wallet.restore_balance(int(snapshot["wallet"])) _bag.replace_all_items(snapshot["bag"]) + _bag.replace_unlocked_bait_ids(snapshot["unlocked_bait_ids"]) _inventory.replace_all_catches( snapshot["catches"], int(snapshot["next_sequence"]) ) diff --git a/network/network_player_animation_protocol.gd b/network/network_player_animation_protocol.gd new file mode 100644 index 0000000..b27cb9d --- /dev/null +++ b/network/network_player_animation_protocol.gd @@ -0,0 +1,94 @@ +class_name NetworkPlayerAnimationProtocol +extends RefCounted + +const FORMAT_VERSION: int = 1 +const MAX_STATE_ID_LENGTH: int = 64 +const MAX_ACTION_SEQUENCE: int = 2147483647 +const MAX_ACTION_ELAPSED_SECONDS: float = 86400.0 + +const LOCOMOTION_IDLE: StringName = &"idle" +const LOCOMOTION_WALKING: StringName = &"walking" +const LOCOMOTION_RUNNING: StringName = &"running" + + +static func make_state( + locomotion_id: StringName, + grounded: bool, + action_id: StringName = &"", + action_sequence: int = 0, + action_elapsed: float = 0.0, +) -> Dictionary: + return { + "format_version": FORMAT_VERSION, + "locomotion_id": String(locomotion_id), + "grounded": grounded, + "action": make_action_state( + action_id, action_sequence, action_elapsed + ), + } + + +static func make_action_state( + action_id: StringName = &"", + action_sequence: int = 0, + action_elapsed: float = 0.0, +) -> Dictionary: + return { + "id": String(action_id), + "sequence": action_sequence, + "elapsed": action_elapsed, + } + + +static func validate_state(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var state: Dictionary = value + return ( + typeof(state.get("format_version")) == TYPE_INT + and int(state["format_version"]) == FORMAT_VERSION + and valid_state_id(state.get("locomotion_id"), false) + and typeof(state.get("grounded")) == TYPE_BOOL + and validate_action_state(state.get("action")) + ) + + +static func validate_action_state(value: Variant) -> bool: + if typeof(value) != TYPE_DICTIONARY: + return false + var action: Dictionary = value + if ( + not valid_state_id(action.get("id"), true) + or typeof(action.get("sequence")) != TYPE_INT + or typeof(action.get("elapsed")) not in [TYPE_FLOAT, TYPE_INT] + ): + return false + var sequence: int = int(action["sequence"]) + var elapsed: float = float(action["elapsed"]) + return ( + sequence >= 0 + and sequence <= MAX_ACTION_SEQUENCE + and is_finite(elapsed) + and elapsed >= 0.0 + and elapsed <= MAX_ACTION_ELAPSED_SECONDS + ) + + +static func valid_state_id(value: Variant, allow_empty: bool) -> bool: + if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]: + return false + var state_id: String = str(value) + if state_id.is_empty(): + return allow_empty + if state_id.length() > MAX_STATE_ID_LENGTH: + return false + for index: int in state_id.length(): + var codepoint: int = state_id.unicode_at(index) + var valid_character: bool = ( + codepoint >= 97 and codepoint <= 122 + or codepoint >= 48 and codepoint <= 57 + or codepoint == 95 + ) + if not valid_character: + return false + return true diff --git a/network/network_player_animation_protocol.gd.uid b/network/network_player_animation_protocol.gd.uid new file mode 100644 index 0000000..466ad02 --- /dev/null +++ b/network/network_player_animation_protocol.gd.uid @@ -0,0 +1 @@ +uid://cmaaep4n3p2tq diff --git a/network/network_player_list_service.gd b/network/network_player_list_service.gd index 6f65e12..4742d14 100644 --- a/network/network_player_list_service.gd +++ b/network/network_player_list_service.gd @@ -15,6 +15,9 @@ var _surface_drawing: NetworkSurfaceDrawingService var _revision := 0 var _host_block_pairs: Dictionary[String, bool] = {} var _peer_fingerprints: Dictionary[int, String] = {} +var _remote_bans: Array[Dictionary] = [] +var _ban_snapshot_requested: bool = false +var _ban_snapshot_loaded: bool = false func setup( @@ -40,6 +43,7 @@ func setup( func(_count: int, _maximum: int) -> void: _sync_authenticated_peers() ) _session.state_changed.connect(_on_session_state_changed) + _session.operator_status_changed.connect(_on_operator_status_changed) _relationships.relationship_changed.connect(_on_relationship_changed) _bans.bans_changed.connect(_changed) _chat.set_relationship_store(_relationships) @@ -64,13 +68,24 @@ func get_entries() -> Array[PlayerListEntry]: entry.compact_fingerprint = NetworkIdentityCrypto.compact_suffix(record.identity_fingerprint) entry.display_name = record.display_name entry.is_host = peer_id == 1 + entry.is_operator = _session.is_peer_operator(peer_id) entry.is_local_player = peer_id == local_id entry.continuity_state = _known.identity_status(record.identity_fingerprint, record.display_name) entry.ping_to_host_ms = _session.get_peer_rtt_ms(peer_id) entry.muted = _relationships.is_muted(record.identity_fingerprint) entry.blocked = blocked - entry.can_kick = _session.is_host() and peer_id != local_id and peer_id != 1 + entry.can_kick = ( + _session.can_local_moderate() + and peer_id != local_id + and peer_id != 1 + and (_session.is_host() or not entry.is_operator) + ) entry.can_ban = entry.can_kick + entry.can_manage_operator = ( + _session.can_manage_operators() + and peer_id != local_id + and peer_id != 1 + ) entry.revision = _revision result.append(entry) result.sort_custom(_entry_before) @@ -89,12 +104,33 @@ func is_local_host() -> bool: return _session.is_host() +func is_local_moderator() -> bool: + return _session.can_local_moderate() + + +func can_manage_operators() -> bool: + return _session.can_manage_operators() + + +func is_open_host() -> bool: + return _session.is_open_host() + + +func set_host_open(is_open: bool) -> bool: + return _session.set_host_open(is_open) + + func get_relationships() -> Array[Dictionary]: return _relationships.get_records() func get_bans() -> Array[Dictionary]: - return _bans.get_bans(_session.get_host_identity_fingerprint()) if _session.is_host() else [] + if _session.is_host(): + return _bans.get_bans(_session.get_host_identity_fingerprint()) + if not _session.is_local_operator(): + return [] + _request_ban_snapshot() + return _remote_bans.duplicate(true) func set_surface_drawing_service( @@ -123,14 +159,12 @@ func get_session_artwork_counts() -> Vector2i: func reset_session_artwork() -> bool: - if not _session.is_host() or _surface_drawing == null: + if _session.is_host(): + return _reset_session_artwork_on_host() + if not _session.is_local_operator(): return false - var ok: bool = _surface_drawing.clear_session_artwork() - moderation_finished.emit( - ok, - "Session artwork cleared." if ok else "Session artwork could not be cleared.", - ) - return ok + request_reset_session_artwork.rpc_id(1) + return true func set_muted(fingerprint: String, display_name: String, value: bool) -> bool: @@ -146,12 +180,13 @@ func set_blocked(fingerprint: String, display_name: String, value: bool) -> bool func kick(peer_id: int, fingerprint: String, revision: int) -> bool: - if not _valid_moderation_target(peer_id, fingerprint, revision): - moderation_finished.emit(false, "That player is no longer connected.") + if _session.is_host(): + return _kick_on_host(peer_id, fingerprint, revision, true) + if not _session.is_local_operator(): + moderation_finished.emit(false, "Only the host or an operator can remove players.") return false - var ok := _session.kick_authenticated_peer(peer_id, fingerprint) - moderation_finished.emit(ok, "Player removed." if ok else "Player could not be removed.") - return ok + request_kick.rpc_id(1, peer_id, fingerprint) + return true func ban( @@ -160,22 +195,158 @@ func ban( display_name: String, revision: int, ) -> bool: - if not _valid_moderation_target(peer_id, fingerprint, revision): - moderation_finished.emit(false, "That player is no longer connected.") + if _session.is_host(): + return _ban_on_host( + peer_id, fingerprint, display_name, revision, true + ) + if not _session.is_local_operator(): + moderation_finished.emit(false, "Only the host or an operator can ban players.") return false - var host_fingerprint := _session.get_host_identity_fingerprint() - if not _bans.ban(host_fingerprint, fingerprint, display_name): - moderation_finished.emit(false, "Ban could not be saved.") - return false - var ok := _session.kick_authenticated_peer(peer_id, fingerprint, true) - moderation_finished.emit(ok, "Player banned." if ok else "Ban saved.") + request_ban.rpc_id(1, peer_id, fingerprint) return true func unban(fingerprint: String) -> bool: - if not _session.is_host(): + if _session.is_host(): + var ok: bool = _bans.unban( + _session.get_host_identity_fingerprint(), fingerprint + ) + moderation_finished.emit( + ok, "Player unbanned." if ok else "Ban could not be removed." + ) + return ok + if not _session.is_local_operator(): + moderation_finished.emit(false, "Only the host or an operator can remove bans.") return false - return _bans.unban(_session.get_host_identity_fingerprint(), fingerprint) + request_unban.rpc_id(1, fingerprint) + return true + + +func set_operator( + peer_id: int, + fingerprint: String, + enabled: bool, + revision: int, +) -> bool: + if ( + not _session.can_manage_operators() + or not _valid_moderation_target( + peer_id, fingerprint, revision, true, true + ) + ): + moderation_finished.emit(false, "That player is no longer connected.") + return false + var ok: bool = _session.set_peer_operator(peer_id, fingerprint, enabled) + moderation_finished.emit( + ok, + ("Player is now an operator." if enabled else "Operator access removed.") + if ok + else "Operator access could not be changed.", + ) + return ok + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_kick(peer_id: int, fingerprint: String) -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if not _valid_operator_sender(sender_id): + return + var ok: bool = _kick_on_host(peer_id, fingerprint, -1, false) + _send_moderation_result( + sender_id, ok, "Player removed." if ok else "Player could not be removed." + ) + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_ban(peer_id: int, fingerprint: String) -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if not _valid_operator_sender(sender_id): + return + var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id) + var display_name: String = record.display_name if record != null else "Player" + var ok: bool = _ban_on_host( + peer_id, fingerprint, display_name, -1, false + ) + _send_moderation_result( + sender_id, ok, "Player banned." if ok else "Player could not be banned." + ) + _send_ban_snapshot(sender_id) + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_unban(fingerprint: String) -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if ( + not _valid_operator_sender(sender_id) + or not NetworkIdentityCrypto.valid_fingerprint(fingerprint) + ): + return + var ok: bool = _bans.unban( + _session.get_host_identity_fingerprint(), fingerprint + ) + _send_moderation_result( + sender_id, ok, "Player unbanned." if ok else "Ban could not be removed." + ) + _send_ban_snapshot(sender_id) + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_reset_session_artwork() -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if not _valid_operator_sender(sender_id): + return + var ok: bool = _reset_session_artwork_on_host(false) + _send_moderation_result( + sender_id, + ok, + "Session artwork cleared." + if ok + else "Session artwork could not be cleared.", + ) + + +@rpc("any_peer", "call_remote", "reliable", 0) +func request_ban_snapshot() -> void: + var sender_id: int = multiplayer.get_remote_sender_id() + if _valid_operator_sender(sender_id): + _send_ban_snapshot(sender_id) + + +@rpc("authority", "call_remote", "reliable", 0) +func receive_moderation_result(success: bool, message: String) -> void: + if not _session.is_joined_client(): + return + moderation_finished.emit(success, message.left(120)) + + +@rpc("authority", "call_remote", "reliable", 0) +func receive_ban_snapshot(records: Array) -> void: + if not _session.is_local_operator() or records.size() > HostBanStore.MAX_BANS: + return + var sanitized: Array[Dictionary] = [] + for value: Variant in records: + if typeof(value) != TYPE_DICTIONARY: + return + var record: Dictionary = value + var fingerprint: String = str(record.get("target_fingerprint", "")) + var display_name: String = str( + record.get("last_known_display_name", "Player") + ).strip_edges().left(NetworkProtocol.MAX_DISPLAY_NAME_LENGTH) + if ( + not NetworkIdentityCrypto.valid_fingerprint(fingerprint) + or display_name.is_empty() + or typeof(record.get("banned_unix")) != TYPE_INT + ): + return + sanitized.append({ + "target_fingerprint": fingerprint, + "last_known_display_name": display_name, + "banned_unix": int(record["banned_unix"]), + }) + _remote_bans = sanitized + _ban_snapshot_requested = false + _ban_snapshot_loaded = true + _changed() func is_locally_blocked(fingerprint: String) -> bool: @@ -263,6 +434,22 @@ func _on_session_state_changed(state: NetworkSession.State) -> void: ]: _host_block_pairs.clear() _peer_fingerprints.clear() + _remote_bans.clear() + _ban_snapshot_requested = false + _ban_snapshot_loaded = false + elif state == NetworkSession.State.JOINED_CLIENT: + _request_ban_snapshot() + _changed() + + +func _on_operator_status_changed(peer_id: int, enabled: bool) -> void: + if peer_id == _session.get_local_peer_id(): + if enabled: + _request_ban_snapshot() + else: + _remote_bans.clear() + _ban_snapshot_requested = false + _ban_snapshot_loaded = false _changed() @@ -287,16 +474,137 @@ func _peer_for_fingerprint(fingerprint: String) -> int: return 0 -func _valid_moderation_target(peer_id: int, fingerprint: String, revision: int) -> bool: - if not _session.is_host() or revision != _revision or peer_id == 1: +func _valid_moderation_target( + peer_id: int, + fingerprint: String, + revision: int, + require_revision: bool, + can_target_operator: bool, +) -> bool: + if ( + not _session.is_host() + or (require_revision and revision != _revision) + or peer_id == 1 + or (not can_target_operator and _session.is_peer_operator(peer_id)) + ): return false var record := _session.get_peer_record(peer_id) return record != null and record.identity_fingerprint == fingerprint +func _valid_operator_sender(peer_id: int) -> bool: + return ( + _session.is_host() + and peer_id > 1 + and _session.is_authenticated_peer(peer_id) + and _session.is_peer_operator(peer_id) + ) + + +func _kick_on_host( + peer_id: int, + fingerprint: String, + revision: int, + local_host_request: bool, +) -> bool: + if not _valid_moderation_target( + peer_id, + fingerprint, + revision, + local_host_request, + local_host_request, + ): + if local_host_request: + moderation_finished.emit(false, "That player is no longer connected.") + return false + var ok: bool = _session.kick_authenticated_peer(peer_id, fingerprint) + if local_host_request: + moderation_finished.emit( + ok, "Player removed." if ok else "Player could not be removed." + ) + return ok + + +func _ban_on_host( + peer_id: int, + fingerprint: String, + display_name: String, + revision: int, + local_host_request: bool, +) -> bool: + if not _valid_moderation_target( + peer_id, + fingerprint, + revision, + local_host_request, + local_host_request, + ): + if local_host_request: + moderation_finished.emit(false, "That player is no longer connected.") + return false + var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id) + var trusted_display_name: String = ( + record.display_name if record != null else display_name + ) + var host_fingerprint: String = _session.get_host_identity_fingerprint() + if not _bans.ban(host_fingerprint, fingerprint, trusted_display_name): + if local_host_request: + moderation_finished.emit(false, "Ban could not be saved.") + return false + var ok: bool = _session.kick_authenticated_peer(peer_id, fingerprint, true) + if local_host_request: + moderation_finished.emit(ok, "Player banned." if ok else "Ban saved.") + return true + + +func _reset_session_artwork_on_host( + emit_local_result: bool = true, +) -> bool: + if not _session.is_host() or _surface_drawing == null: + return false + var ok: bool = _surface_drawing.clear_session_artwork() + if emit_local_result: + moderation_finished.emit( + ok, + "Session artwork cleared." + if ok + else "Session artwork could not be cleared.", + ) + return ok + + +func _send_moderation_result( + peer_id: int, + success: bool, + message: String, +) -> void: + receive_moderation_result.rpc_id(peer_id, success, message.left(120)) + + +func _send_ban_snapshot(peer_id: int) -> void: + if not _valid_operator_sender(peer_id): + return + receive_ban_snapshot.rpc_id( + peer_id, + _bans.get_bans(_session.get_host_identity_fingerprint()), + ) + + +func _request_ban_snapshot() -> void: + if ( + _session.is_local_operator() + and not _ban_snapshot_requested + and not _ban_snapshot_loaded + ): + _ban_snapshot_requested = true + request_ban_snapshot.rpc_id(1) + + func _entry_before(a: PlayerListEntry, b: PlayerListEntry) -> bool: if a.is_host != b.is_host: return a.is_host + if a.is_operator != b.is_operator: + return a.is_operator if a.is_local_player != b.is_local_player: return a.is_local_player var compared := a.display_name.naturalnocasecmp_to(b.display_name) diff --git a/network/network_profile_preferences.gd b/network/network_profile_preferences.gd index c90cd7d..5ecb6fd 100644 --- a/network/network_profile_preferences.gd +++ b/network/network_profile_preferences.gd @@ -2,8 +2,14 @@ class_name NetworkProfilePreferences extends Node const FORMAT_VERSION: int = 1 +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) var profile_id: String = "" var display_name: String = "Player" +var voice_id: String = VoiceProfilesType.DEFAULT_ID +var speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID +var call_id: String = VoiceProfilesType.DEFAULT_CALL_ID var created_at_unix: int = 0 var _profile_path := "" var _expected_hash := "" @@ -48,19 +54,45 @@ func load_or_create() -> bool: Time.get_ticks_usec(), ] display_name = "Player" + voice_id = VoiceProfilesType.DEFAULT_ID + speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID + call_id = VoiceProfilesType.DEFAULT_CALL_ID created_at_unix = int(Time.get_unix_time_from_system()) return _save_atomic() func set_display_name(value: String) -> bool: + return set_profile_identity(value, voice_id, speech_speed_id, call_id) + + +func set_profile_identity( + value: String, + selected_voice_id: String, + selected_speech_speed_id: String, + selected_call_id: String, +) -> bool: var clean_name: String = value.strip_edges() - if not is_valid_display_name(clean_name): + if ( + not is_valid_display_name(clean_name) + or not VoiceProfilesType.is_valid(selected_voice_id) + or not VoiceProfilesType.is_valid_speed(selected_speech_speed_id) + or not VoiceProfilesType.is_valid_call(selected_call_id) + ): return false - var previous: String = display_name + var previous_name: String = display_name + var previous_voice_id: String = voice_id + var previous_speech_speed_id: String = speech_speed_id + var previous_call_id: String = call_id display_name = clean_name + voice_id = selected_voice_id + speech_speed_id = selected_speech_speed_id + call_id = selected_call_id if _save_atomic(): return true - display_name = previous + display_name = previous_name + voice_id = previous_voice_id + speech_speed_id = previous_speech_speed_id + call_id = previous_call_id return false @@ -109,6 +141,15 @@ func _load_existing() -> bool: return false profile_id = loaded_id display_name = loaded_name + voice_id = VoiceProfilesType.sanitized_id( + str(data.get("voice_id", VoiceProfilesType.DEFAULT_ID)) + ) + speech_speed_id = VoiceProfilesType.sanitized_speed_id( + str(data.get("speech_speed_id", VoiceProfilesType.DEFAULT_SPEED_ID)) + ) + call_id = VoiceProfilesType.sanitized_call_id( + str(data.get("call_id", VoiceProfilesType.DEFAULT_CALL_ID)) + ) created_at_unix = int(data["created_at_unix"]) return true @@ -118,6 +159,9 @@ func _save_atomic() -> bool: "format_version": FORMAT_VERSION, "profile_id": profile_id, "display_name": display_name, + "voice_id": voice_id, + "speech_speed_id": speech_speed_id, + "call_id": call_id, "created_at_unix": created_at_unix, } var result := PortableFileGuard.write_guarded( diff --git a/network/network_profile_protocol.gd b/network/network_profile_protocol.gd index ac4dec5..32c44b6 100644 --- a/network/network_profile_protocol.gd +++ b/network/network_profile_protocol.gd @@ -49,7 +49,17 @@ static func signature_fields(data: Dictionary) -> Array: str(data.get("sender_fingerprint", "")), str(data.get("display_name", "")), str(appearance.get("species", "")), + CharacterCustomizationCatalog.character_scale_percent( + appearance.get( + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ), str(appearance.get("fur_pattern", "")), + str(appearance.get("fur_style", "")), + str(appearance.get("fur_color_2", "")), + str(appearance.get("fur_color_3", "")), + str(appearance.get("fur_color_4", "")), str(appearance.get("ears", "")), str(appearance.get("eyes", "")), str(appearance.get("nose", "")), diff --git a/network/network_profile_service.gd b/network/network_profile_service.gd index 2666d26..86b1be1 100644 --- a/network/network_profile_service.gd +++ b/network/network_profile_service.gd @@ -1,6 +1,10 @@ class_name NetworkProfileService extends Node +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) + signal conflict_result( request_id: String, has_conflict: bool, @@ -18,6 +22,9 @@ var _preferences: NetworkProfilePreferences var _appearance_store: PlayerAppearanceStore var _spawn_service: PlayerSpawnService var _pending_apply: Dictionary[String, Dictionary] = {} +var _pending_voice_ids: Dictionary[String, String] = {} +var _pending_speech_speed_ids: Dictionary[String, String] = {} +var _pending_call_ids: Dictionary[String, String] = {} var _host_pending_apply: Dictionary[String, Dictionary] = {} var _latest_check_id: String = "" var _latest_check_name: String = "" @@ -40,6 +47,7 @@ func setup( _session.join_authenticated.connect(_on_join_authenticated) _session.state_changed.connect(_on_session_state_changed) _apply_to_avatar(1, _appearance_store.get_snapshot()) + _apply_local_voice_to_avatar() func get_persisted_name() -> String: @@ -50,6 +58,18 @@ func get_persisted_appearance() -> Dictionary: return _appearance_store.get_snapshot() +func get_persisted_voice_id() -> String: + return _preferences.voice_id + + +func get_persisted_speech_speed_id() -> String: + return _preferences.speech_speed_id + + +func get_persisted_call_id() -> String: + return _preferences.call_id + + func get_identity_fingerprint() -> String: return ( _session.get_local_identity_fingerprint() @@ -87,11 +107,17 @@ func apply_profile( display_name: String, appearance: Dictionary, use_anyway: bool, + voice_id: String = VoiceProfilesType.DEFAULT_ID, + speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID, + call_id: String = VoiceProfilesType.DEFAULT_CALL_ID, ) -> bool: var clean_name := display_name.strip_edges() if ( not NetworkProfilePreferences.is_valid_display_name(clean_name) or not CharacterCustomizationCatalog.validate_snapshot(appearance) + or not VoiceProfilesType.is_valid(voice_id) + or not VoiceProfilesType.is_valid_speed(speech_speed_id) + or not VoiceProfilesType.is_valid_call(call_id) ): apply_finished.emit(false, "Check the player name and appearance choices.") return false @@ -108,6 +134,9 @@ func apply_profile( "profile_update", NetworkProfileProtocol.signature_fields(request) ) _pending_apply[request_id] = request + _pending_voice_ids[request_id] = voice_id + _pending_speech_speed_ids[request_id] = speech_speed_id + _pending_call_ids[request_id] = call_id if _session == null or not _session.is_gameplay_session_active(): _apply_local_result(request_id, true, "", false, PackedStringArray()) elif _session.is_host(): @@ -264,20 +293,57 @@ func _apply_local_result( return if not accepted: _pending_apply.erase(request_id) + _pending_voice_ids.erase(request_id) + _pending_speech_speed_ids.erase(request_id) + _pending_call_ids.erase(request_id) conflict_result.emit(request_id, conflict, suggestions) apply_finished.emit(false, message) return var previous_name := _preferences.display_name - if not _preferences.set_display_name(str(request["display_name"])): + var previous_voice_id := _preferences.voice_id + var previous_speech_speed_id := _preferences.speech_speed_id + var previous_call_id := _preferences.call_id + var requested_voice_id: String = _pending_voice_ids.get( + request_id, + VoiceProfilesType.DEFAULT_ID, + ) + var requested_speech_speed_id: String = _pending_speech_speed_ids.get( + request_id, + VoiceProfilesType.DEFAULT_SPEED_ID, + ) + var requested_call_id: String = _pending_call_ids.get( + request_id, + VoiceProfilesType.DEFAULT_CALL_ID, + ) + if not _preferences.set_profile_identity( + str(request["display_name"]), + requested_voice_id, + requested_speech_speed_id, + requested_call_id, + ): _pending_apply.erase(request_id) + _pending_voice_ids.erase(request_id) + _pending_speech_speed_ids.erase(request_id) + _pending_call_ids.erase(request_id) apply_finished.emit(false, "Profile could not be saved.") return if not _appearance_store.save_snapshot(request["appearance"]): - _preferences.set_display_name(previous_name) + _preferences.set_profile_identity( + previous_name, + previous_voice_id, + previous_speech_speed_id, + previous_call_id, + ) _pending_apply.erase(request_id) + _pending_voice_ids.erase(request_id) + _pending_speech_speed_ids.erase(request_id) + _pending_call_ids.erase(request_id) apply_finished.emit(false, "Profile could not be saved.") return _pending_apply.erase(request_id) + _pending_voice_ids.erase(request_id) + _pending_speech_speed_ids.erase(request_id) + _pending_call_ids.erase(request_id) if _session != null and _session.is_gameplay_session_active(): if _session.is_host(): _session.apply_canonical_profile( @@ -304,6 +370,7 @@ func _apply_local_result( _session.get_local_peer_id() if _session != null else 1, _appearance_store.get_snapshot(), ) + _apply_local_voice_to_avatar() apply_finished.emit(true, "Profile saved.") @@ -361,6 +428,7 @@ func _on_join_authenticated() -> void: _apply_to_avatar( _session.get_local_peer_id(), _appearance_store.get_snapshot() ) + _apply_local_voice_to_avatar() func _on_peer_removed(_peer_id: int) -> void: @@ -400,6 +468,9 @@ func _refresh_latest_check() -> void: func _on_session_state_changed(state: NetworkSession.State) -> void: if state == NetworkSession.State.INACTIVE: _pending_apply.clear() + _pending_voice_ids.clear() + _pending_speech_speed_ids.clear() + _pending_call_ids.clear() _host_pending_apply.clear() _latest_check_id = "" _latest_check_name = "" @@ -465,5 +536,16 @@ func _apply_to_avatar(peer_id: int, appearance: Dictionary) -> void: avatar.apply_appearance_snapshot(appearance) +func _apply_local_voice_to_avatar() -> void: + if _spawn_service == null: + return + var local_peer_id := ( + _session.get_local_peer_id() if _session != null else 1 + ) + var avatar := _spawn_service.get_avatar(local_peer_id) + if avatar != null: + avatar.apply_animalese_voice_id(_preferences.voice_id) + + func _new_id() -> String: return Crypto.new().generate_random_bytes(16).hex_encode() diff --git a/network/network_protocol.gd b/network/network_protocol.gd index 94236b7..7250fba 100644 --- a/network/network_protocol.gd +++ b/network/network_protocol.gd @@ -1,8 +1,9 @@ class_name NetworkProtocol extends RefCounted -const PROTOCOL_VERSION: int = 3 +const PROTOCOL_VERSION: int = 4 const GAME_BUILD: String = "prealpha" +const MAX_GAME_VERSION_LENGTH: int = 64 const MAX_DISPLAY_NAME_LENGTH: int = 24 const MAX_PROFILE_ID_LENGTH: int = 96 const MAX_NONCE_LENGTH: int = 96 @@ -23,6 +24,7 @@ const ART_SHOP_CAPABILITY: String = "art_shop_v1" const WORLD_TIME_CAPABILITY: String = "world_time_v1" const WORLD_WEATHER_CAPABILITY: String = "world_weather_v1" const FISH_QUALITY_CAPABILITY: String = "fish_quality_v1" +const JOBS_CAPABILITY: String = "jobs_v1" enum RejectionCode { NONE, @@ -36,9 +38,80 @@ enum RejectionCode { SERVER_SHUTTING_DOWN, UNSUPPORTED_CLIENT, BANNED, + CLIENT_OUTDATED, + SERVER_OUTDATED, + VERSION_MISMATCH, } +static func game_version() -> String: + return str(ProjectSettings.get_setting( + "application/config/version", "unknown" + )).strip_edges() + + +static func game_version_rejection( + client_version: String, + server_version: String, +) -> RejectionCode: + if client_version == server_version: + return RejectionCode.NONE + var comparison: int = _compare_game_versions( + client_version, server_version + ) + if comparison < 0: + return RejectionCode.CLIENT_OUTDATED + if comparison > 0: + return RejectionCode.SERVER_OUTDATED + return RejectionCode.VERSION_MISMATCH + + +static func _compare_game_versions(first: String, second: String) -> int: + var first_parts: PackedStringArray = first.split("-", true, 1) + var second_parts: PackedStringArray = second.split("-", true, 1) + var first_core: PackedStringArray = first_parts[0].split(".") + var second_core: PackedStringArray = second_parts[0].split(".") + for index: int in maxi(first_core.size(), second_core.size()): + var first_number: int = ( + int(first_core[index]) + if index < first_core.size() and first_core[index].is_valid_int() + else 0 + ) + var second_number: int = ( + int(second_core[index]) + if index < second_core.size() and second_core[index].is_valid_int() + else 0 + ) + if first_number != second_number: + return -1 if first_number < second_number else 1 + var first_rank: int = _prerelease_rank( + first_parts[1] if first_parts.size() > 1 else "" + ) + var second_rank: int = _prerelease_rank( + second_parts[1] if second_parts.size() > 1 else "" + ) + if first_rank != second_rank: + return -1 if first_rank < second_rank else 1 + return 0 + + +static func _prerelease_rank(label: String) -> int: + var normalized: String = label.to_lower() + if normalized.is_empty(): + return 5 + if normalized.begins_with("dev"): + return 0 + if normalized.begins_with("prealpha"): + return 1 + if normalized.begins_with("alpha"): + return 2 + if normalized.begins_with("beta"): + return 3 + if normalized.begins_with("rc"): + return 4 + return 2 + + static func make_identity_hello( public_key: String, fingerprint: String, @@ -47,6 +120,7 @@ static func make_identity_hello( ) -> Dictionary: return { "protocol_version": PROTOCOL_VERSION, + "game_version": game_version(), "public_key": public_key, "fingerprint": fingerprint, "client_nonce": client_nonce, @@ -61,6 +135,9 @@ static func validate_identity_hello(data: Variant) -> bool: var value: Dictionary = data return ( typeof(value.get("protocol_version")) == TYPE_INT + and typeof(value.get("game_version")) == TYPE_STRING + and not str(value["game_version"]).is_empty() + and str(value["game_version"]).length() <= MAX_GAME_VERSION_LENGTH and typeof(value.get("public_key")) == TYPE_STRING and str(value["public_key"]).to_utf8_buffer().size() <= MAX_PUBLIC_KEY_LENGTH and NetworkIdentityCrypto.valid_fingerprint(value.get("fingerprint")) @@ -84,6 +161,7 @@ static func make_client_hello( ) -> Dictionary: return { "protocol_version": PROTOCOL_VERSION, + "game_version": game_version(), "game_build": GAME_BUILD, "local_profile_id": profile_id, "display_name": display_name, @@ -93,6 +171,7 @@ static func make_client_hello( SURFACE_DRAWING_CAPABILITY, WORLD_TIME_CAPABILITY, WORLD_WEATHER_CAPABILITY, + JOBS_CAPABILITY, ]), "cosmetic_snapshot": cosmetic_snapshot, "identity_fingerprint": identity_fingerprint, @@ -106,6 +185,7 @@ static func validate_client_hello(data: Variant) -> String: var payload: Dictionary = data for key: String in [ "protocol_version", + "game_version", "game_build", "local_profile_id", "display_name", @@ -119,6 +199,12 @@ static func validate_client_hello(data: Variant) -> String: return "Handshake is missing %s." % key if typeof(payload["protocol_version"]) != TYPE_INT: return "Protocol version is invalid." + if ( + typeof(payload["game_version"]) != TYPE_STRING + or str(payload["game_version"]).is_empty() + or str(payload["game_version"]).length() > MAX_GAME_VERSION_LENGTH + ): + return "Game version is invalid." if typeof(payload["game_build"]) != TYPE_STRING: return "Game build is invalid." if typeof(payload["local_profile_id"]) != TYPE_STRING: @@ -169,12 +255,23 @@ static func validate_client_hello(data: Variant) -> String: static func client_profile_fields(data: Dictionary) -> Array: var appearance: Dictionary = data.get("cosmetic_snapshot", {}) return [ + str(data.get("game_version", "")), str(data.get("client_nonce", "")), str(data.get("identity_fingerprint", "")), str(data.get("local_profile_id", "")), str(data.get("display_name", "")), str(appearance.get("species", "")), + CharacterCustomizationCatalog.character_scale_percent( + appearance.get( + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ), str(appearance.get("fur_pattern", "")), + str(appearance.get("fur_style", "")), + str(appearance.get("fur_color_2", "")), + str(appearance.get("fur_color_3", "")), + str(appearance.get("fur_color_4", "")), str(appearance.get("ears", "")), str(appearance.get("eyes", "")), str(appearance.get("nose", "")), @@ -190,14 +287,16 @@ static func make_server_hello( assigned_peer_id: int, player_count: int, max_players: int, + server_display_name: String = "NETfishing", ) -> Dictionary: return { "accepted": accepted, "rejection_code": int(rejection_code), "protocol_version": PROTOCOL_VERSION, + "game_version": game_version(), "session_id": session_id, "assigned_peer_id": assigned_peer_id, - "server_display_name": "NETfishing", + "server_display_name": server_display_name, "player_count": player_count, "max_players": max_players, "capability_flags": PackedStringArray([ @@ -213,6 +312,7 @@ static func make_server_hello( SURFACE_DRAWING_CAPABILITY, WORLD_TIME_CAPABILITY, WORLD_WEATHER_CAPABILITY, + JOBS_CAPABILITY, "chat_v1", "mail_v1", "profile_v1", @@ -241,5 +341,20 @@ static func rejection_text(code: int) -> String: return "This game build is not supported by the server." RejectionCode.BANNED: return "You are not permitted to join this server." + RejectionCode.CLIENT_OUTDATED: + return ( + "Your NETfishing version is out of date. " + + "Update the game to join this server." + ) + RejectionCode.SERVER_OUTDATED: + return ( + "This server is out of date. " + + "The host needs to update NETfishing." + ) + RejectionCode.VERSION_MISMATCH: + return ( + "This game and server use different NETfishing versions. " + + "Update both to the latest release." + ) _: return "The server rejected the connection." diff --git a/network/network_session.gd b/network/network_session.gd index 8c948af..076acc5 100644 --- a/network/network_session.gd +++ b/network/network_session.gd @@ -2,12 +2,14 @@ class_name NetworkSession extends Node const DEFAULT_PORT: int = 7777 +const DEFAULT_PRIVATE_HOST_PORT_ATTEMPTS: int = 16 const DEFAULT_SESSION_MAX_PLAYERS: int = 8 const DEFAULT_TRANSPORT_MAX_CLIENTS: int = 31 const CONNECTION_TIMEOUT_SECONDS: float = 10.0 const AUTHENTICATION_TIMEOUT_SECONDS: float = 60.0 const INPUT_INTERVAL: float = 1.0 / 30.0 -const SNAPSHOT_INTERVAL: float = 1.0 / 20.0 +const SNAPSHOT_INTERVAL: float = 1.0 / 30.0 +const MAX_MOVEMENT_INPUT_SEQUENCE: int = 2147483647 signal state_changed(state: State) signal status_message_changed(message: String) @@ -15,6 +17,7 @@ signal connection_error(message: String) signal peer_authenticated(peer_id: int, display_name: String) signal peer_removed(peer_id: int) signal host_openness_changed(is_open: bool) +signal session_display_name_changed(display_name: String) signal peer_count_changed(player_count: int, max_players: int) signal peer_display_name_changed(peer_id: int, display_name: String) signal peer_profile_changed( @@ -30,7 +33,8 @@ signal server_trust_required( is_changed: bool, ) signal peer_identity_observed(peer_id: int, status: String) -signal server_lost +signal operator_status_changed(peer_id: int, is_operator: bool) +signal server_lost(message: String) signal remote_recovery_requested(peer_id: int, entry_position: Vector3) signal remote_recovery_presentation_changed( peer_id: int, @@ -97,6 +101,13 @@ var _local_appearance_snapshot: Dictionary = ( CharacterCustomizationCatalog.default_snapshot() ) var _moderation_disconnect_message := "" +var _host_port: int = 0 +var _session_display_name: String = "NETfishing Room" +var _dedicated_host: bool = false +var _configured_operator_fingerprints: Dictionary[String, bool] = {} +var _session_operator_fingerprints: Dictionary[String, bool] = {} +var _operator_peer_ids: Dictionary[int, bool] = {} +var _local_operator: bool = false func _ready() -> void: @@ -116,6 +127,7 @@ func setup( known_players: KnownPlayerStore, server_trust: ServerTrustStore, host_bans: HostBanStore, + dedicated: bool = false, ) -> void: _profile = profile _saved_servers = saved_servers @@ -125,13 +137,30 @@ func setup( _known_players = known_players _server_trust = server_trust _host_bans = host_bans - if _profile != null: + if not dedicated and _profile != null: _profile_ready = _profile.load_or_create() - if _player_identity != null: + if not dedicated and _player_identity != null: _profile_ready = _profile_ready and _player_identity.load_or_create() -func start_private_host(port: int = DEFAULT_PORT) -> bool: +func configure_dedicated_operators( + fingerprints: PackedStringArray, +) -> bool: + if state != State.INACTIVE: + return false + var configured: Dictionary[String, bool] = {} + for fingerprint: String in fingerprints: + if not NetworkIdentityCrypto.valid_fingerprint(fingerprint): + return false + configured[fingerprint] = true + _configured_operator_fingerprints = configured + return true + + +func start_private_host( + port: int = DEFAULT_PORT, + port_attempts: int = 1, +) -> bool: if ( state != State.INACTIVE or not _profile_ready @@ -140,27 +169,91 @@ func start_private_host(port: int = DEFAULT_PORT) -> bool: or _host_identity == null ): return false + return _start_host(port, port_attempts, false) + + +func start_dedicated_host( + port: int = DEFAULT_PORT, + max_players: int = DEFAULT_SESSION_MAX_PLAYERS, + bind_address: String = "*", +) -> bool: + if ( + state != State.INACTIVE + or _spawn_service == null + or _host_identity == null + or max_players < 1 + or max_players > 128 + or bind_address.is_empty() + ): + return false + session_max_players = max_players + transport_max_clients = maxi(transport_max_clients, max_players) + return _start_host(port, 1, true, bind_address) + + +func _start_host( + port: int, + port_attempts: int, + dedicated: bool, + bind_address: String = "*", +) -> bool: if not _host_identity.load_or_create(): _fail(_host_identity.error_message) return false - if port < 1 or port > 65535: + if port < 1 or port > 65535 or port_attempts < 1: _fail("The hosting port must be from 1 to 65535.") return false _operation_generation += 1 - _set_state(State.STARTING_PRIVATE_HOST, "Starting private game...") - _replace_transport() - var error: Error = _transport.start_host( - port, - transport_max_clients, + _set_state( + State.STARTING_PRIVATE_HOST, + "Starting dedicated server..." if dedicated else "Starting private game...", ) - if error != OK: - _fail("Could not start a private game on UDP port %d." % port) + var selected_port: int = 0 + var final_port: int = mini(port + port_attempts - 1, 65535) + for candidate_port: int in range(port, final_port + 1): + if not _can_bind_udp_port(candidate_port, bind_address): + continue + _replace_transport() + var error: Error = _transport.start_host( + candidate_port, + transport_max_clients, + bind_address, + ) + if error == OK: + selected_port = candidate_port + break + if selected_port == 0: + _fail( + "Could not start a private game on UDP ports %d–%d." + % [port, final_port] + ) return false + _host_port = selected_port + _dedicated_host = dedicated var peer: MultiplayerPeer = _transport.get_multiplayer_peer() peer.refuse_new_connections = true multiplayer.multiplayer_peer = peer _session_id = Crypto.new().generate_random_bytes(16).hex_encode() _registry.clear() + _session_operator_fingerprints.clear() + _operator_peer_ids.clear() + _local_operator = false + _spawn_service.clear_remote_players() + if not dedicated: + _register_player_host() + _set_state( + State.PRIVATE_HOST, + ( + "Dedicated server • UDP %d" + if dedicated else "Private game • UDP %d" + ) % selected_port, + ) + host_openness_changed.emit(false) + _emit_peer_count() + return true + + +func _register_player_host() -> void: _registry.add_peer( 1, _profile.profile_id, @@ -173,6 +266,7 @@ func start_private_host(port: int = DEFAULT_PORT) -> bool: NetworkProtocol.SURFACE_DRAWING_CAPABILITY, NetworkProtocol.WORLD_TIME_CAPABILITY, NetworkProtocol.WORLD_WEATHER_CAPABILITY, + NetworkProtocol.JOBS_CAPABILITY, ]), ) _registry.update_appearance(1, _local_appearance_snapshot) @@ -195,17 +289,104 @@ func start_private_host(port: int = DEFAULT_PORT) -> bool: "client_nonce": host_profile_hello["client_nonce"], } _archive_authenticated_identity(host_record) - _spawn_service.clear_remote_players() _spawn_service.register_local_player(1) var host_avatar := _spawn_service.get_avatar(1) if host_avatar != null: host_avatar.apply_appearance_snapshot(_local_appearance_snapshot) - _set_state(State.PRIVATE_HOST, "Private game • UDP %d" % port) - host_openness_changed.emit(false) - _emit_peer_count() + + +func get_host_port() -> int: + return _host_port if is_host() else 0 + + +func send_traversal_packet( + destination_address: String, + destination_port: int, + packet: PackedByteArray, +) -> bool: + return ( + _transport != null + and state in [ + State.PRIVATE_HOST, + State.OPEN_HOST, + State.CONNECTING, + State.AUTHENTICATING, + ] + and _transport.send_raw_packet( + destination_address, + destination_port, + packet, + ) + ) + + +func is_dedicated_host() -> bool: + return is_host() and _dedicated_host + + +func is_local_operator() -> bool: + return state == State.JOINED_CLIENT and _local_operator + + +func can_local_moderate() -> bool: + return is_host() or is_local_operator() + + +func can_manage_operators() -> bool: + return is_host() and not _dedicated_host + + +func is_peer_operator(peer_id: int) -> bool: + return bool(_operator_peer_ids.get(peer_id, false)) + + +func set_peer_operator( + peer_id: int, + fingerprint: String, + enabled: bool, +) -> bool: + if not can_manage_operators() or peer_id <= 1: + return false + var record: PeerRegistry.PeerRecord = _registry.get_peer(peer_id) + if ( + record == null + or not record.identity_authenticated + or record.identity_fingerprint != fingerprint + ): + return false + if enabled: + _session_operator_fingerprints[fingerprint] = true + else: + _session_operator_fingerprints.erase(fingerprint) + _set_operator_status(peer_id, enabled) return true +func set_session_display_name(value: String) -> void: + var cleaned: String = value.strip_edges().left(48) + if cleaned.is_empty(): + cleaned = "NETfishing Room" + if cleaned == _session_display_name: + return + _session_display_name = cleaned + session_display_name_changed.emit(_session_display_name) + + +func get_session_display_name() -> String: + return _session_display_name + + +func get_local_display_name() -> String: + return _profile.display_name if _profile != null else "" + + +static func _can_bind_udp_port(port: int, bind_address: String = "*") -> bool: + var probe := PacketPeerUDP.new() + var error: Error = probe.bind(port, bind_address) + probe.close() + return error == OK + + func join_direct(endpoint_text: String) -> bool: if ( state != State.INACTIVE @@ -283,10 +464,12 @@ func set_host_open(is_open: bool) -> bool: _set_state( State.OPEN_HOST if is_open else State.PRIVATE_HOST, ( - "Open game • %d / %d players" + "Dedicated server • UDP %d • %d / %d players" + if _dedicated_host + else "Open game • UDP %d • %d / %d players" if is_open - else "Private game • %d / %d players" - ) % [_registry.size(), session_max_players] + else "Private game • UDP %d • %d / %d players" + ) % [_host_port, _registry.size(), session_max_players] ) host_openness_changed.emit(is_open) return true @@ -545,9 +728,13 @@ func apply_canonical_profile( var appearance_changed := _registry.update_appearance(peer_id, appearance) if name_changed: peer_display_name_changed.emit(peer_id, display_name) - if name_changed and appearance_changed: + if appearance_changed: + var avatar: Player = _spawn_service.get_avatar(peer_id) + if avatar != null: + avatar.apply_appearance_snapshot(appearance) + if name_changed or appearance_changed: peer_profile_changed.emit(peer_id, display_name, appearance.duplicate(true)) - return name_changed and appearance_changed + return name_changed or appearance_changed @rpc("any_peer", "call_remote", "reliable", 0) @@ -574,6 +761,8 @@ func _apply_display_name(peer_id: int, value: String) -> void: func get_local_peer_id() -> int: + if is_dedicated_host(): + return 0 return multiplayer.get_unique_id() if is_gameplay_session_active() else 0 @@ -686,7 +875,7 @@ func _on_server_disconnected() -> void: ) _moderation_disconnect_message = "" _set_state(State.SERVER_LOST, message) - server_lost.emit() + server_lost.emit(message) connection_error.emit(message) @@ -694,6 +883,7 @@ func _on_peer_disconnected(peer_id: int) -> void: _pending_authentication.erase(peer_id) _pending_identity_challenges.erase(peer_id) _authenticated_identity_cache.erase(peer_id) + _operator_peer_ids.erase(peer_id) var recovery_attempt: String = _recovery_attempts.get(peer_id, "") if not recovery_attempt.is_empty(): _recovery_attempts.erase(peer_id) @@ -714,6 +904,16 @@ func submit_identity_hello(data: Dictionary) -> void: var sender_id: int = multiplayer.get_remote_sender_id() if not is_host() or sender_id <= 1 or not _pending_authentication.has(sender_id): return + var client_game_version: String = str(data.get("game_version", "")) + var version_rejection: NetworkProtocol.RejectionCode = ( + NetworkProtocol.game_version_rejection( + client_game_version, + NetworkProtocol.game_version(), + ) + ) + if version_rejection != NetworkProtocol.RejectionCode.NONE: + _reject_peer(sender_id, version_rejection) + return if ( not NetworkProtocol.validate_identity_hello(data) or int(data["protocol_version"]) != NetworkProtocol.PROTOCOL_VERSION @@ -744,8 +944,10 @@ func submit_identity_hello(data: Dictionary) -> void: "client_nonce": str(data["client_nonce"]), "client_fingerprint": fingerprint, "client_public_key": public_pem, + "client_game_version": client_game_version, "server_nonce": server_nonce, "server_fingerprint": _host_identity.fingerprint, + "server_game_version": NetworkProtocol.game_version(), "session_id": _session_id, "generation": _operation_generation, "expires_at_msec": Time.get_ticks_msec() + 60000, @@ -762,7 +964,25 @@ func submit_identity_hello(data: Dictionary) -> void: @rpc("authority", "call_remote", "reliable", 0) func receive_server_identity_proof(data: Dictionary) -> void: - if state != State.AUTHENTICATING or not _valid_server_proof_shape(data): + if state != State.AUTHENTICATING: + return + var server_game_version: String = str(data.get("server_game_version", "")) + if server_game_version.is_empty(): + _fail_identity(NetworkProtocol.rejection_text( + NetworkProtocol.RejectionCode.SERVER_OUTDATED + )) + return + var version_rejection: NetworkProtocol.RejectionCode = ( + NetworkProtocol.game_version_rejection( + NetworkProtocol.game_version(), + server_game_version, + ) + ) + if version_rejection != NetworkProtocol.RejectionCode.NONE: + _fail_identity(NetworkProtocol.rejection_text(version_rejection)) + return + if not _valid_server_proof_shape(data): + _fail_identity("The server sent an invalid identity proof.") return if ( str(data["attempt_id"]) != str(_client_identity_attempt.get("attempt_id", "")) @@ -871,6 +1091,7 @@ func submit_client_identity_proof(data: Dictionary) -> void: _authenticated_identity_cache[sender_id] = { "fingerprint": challenge["client_fingerprint"], "public_key": challenge["client_public_key"], + "game_version": challenge["client_game_version"], } if ( _host_bans != null @@ -887,7 +1108,6 @@ func submit_client_identity_proof(data: Dictionary) -> void: @rpc("authority", "call_remote", "reliable", 0) func receive_moderation_disconnect(message: String) -> void: _moderation_disconnect_message = message.left(120) - connection_error.emit(_moderation_disconnect_message) @rpc("authority", "call_remote", "reliable", 0) @@ -917,6 +1137,16 @@ func submit_client_hello(data: Dictionary) -> void: if not validation_error.is_empty(): _reject_peer(sender_id, NetworkProtocol.RejectionCode.MALFORMED_HANDSHAKE) return + var client_game_version: String = str(data.get("game_version", "")) + var version_rejection: NetworkProtocol.RejectionCode = ( + NetworkProtocol.game_version_rejection( + client_game_version, + NetworkProtocol.game_version(), + ) + ) + if version_rejection != NetworkProtocol.RejectionCode.NONE: + _reject_peer(sender_id, version_rejection) + return var client_capabilities: PackedStringArray = _sanitized_capabilities( data.get("capability_flags", []) ) @@ -932,6 +1162,7 @@ func submit_client_hello(data: Dictionary) -> void: return if ( data["identity_fingerprint"] != identity["fingerprint"] + or client_game_version != str(identity.get("game_version", "")) or not NetworkIdentityCrypto.verify_fields( NetworkIdentityCrypto.load_public_key(identity["public_key"]), "handshake_client_profile", @@ -966,6 +1197,11 @@ func submit_client_hello(data: Dictionary) -> void: NetworkProtocol.RejectionCode.MALFORMED_HANDSHAKE ) return + var operator_enabled: bool = _operator_for_fingerprint( + str(identity["fingerprint"]) + ) + if operator_enabled: + _operator_peer_ids[sender_id] = true var submitted_appearance := CharacterCustomizationCatalog.sanitized_snapshot( data["cosmetic_snapshot"] ) @@ -989,6 +1225,14 @@ func submit_client_hello(data: Dictionary) -> void: _spawn_service.get_spawn_transform_for_index(spawn_index) ) _spawn_service.spawn_remote_player(sender_id, spawn_transform, true) + var spawned_avatar: Player = _spawn_service.get_avatar(sender_id) + if spawned_avatar != null: + spawned_avatar.apply_appearance_snapshot(submitted_appearance) + peer_profile_changed.emit( + sender_id, + display_name, + submitted_appearance.duplicate(true), + ) receive_server_hello.rpc_id( sender_id, NetworkProtocol.make_server_hello( @@ -997,13 +1241,17 @@ func submit_client_hello(data: Dictionary) -> void: _session_id, sender_id, _registry.size(), - session_max_players + session_max_players, + _session_display_name, ) ) receive_spawn_list.rpc_id(sender_id, _build_spawn_list()) + receive_operator_snapshot.rpc_id(sender_id, _operator_peer_id_snapshot()) receive_peer_spawn.rpc( _make_spawn_entry(sender_id, display_name, spawn_transform) ) + if operator_enabled: + receive_operator_status.rpc(sender_id, true) peer_authenticated.emit(sender_id, display_name) _emit_peer_count() @@ -1046,6 +1294,7 @@ func receive_server_hello(data: Dictionary) -> void: if ( typeof(data.get("accepted")) != TYPE_BOOL or typeof(data.get("protocol_version")) != TYPE_INT + or typeof(data.get("game_version")) != TYPE_STRING or typeof(data.get("rejection_code")) != TYPE_INT ): _teardown_peer() @@ -1058,6 +1307,16 @@ func receive_server_hello(data: Dictionary) -> void: _teardown_peer() _fail(message) return + var version_rejection: NetworkProtocol.RejectionCode = ( + NetworkProtocol.game_version_rejection( + NetworkProtocol.game_version(), + str(data["game_version"]), + ) + ) + if version_rejection != NetworkProtocol.RejectionCode.NONE: + _teardown_peer() + _fail(NetworkProtocol.rejection_text(version_rejection)) + return if int(data["protocol_version"]) != NetworkProtocol.PROTOCOL_VERSION: _teardown_peer() _fail("The server uses a different network protocol.") @@ -1144,12 +1403,41 @@ func receive_peer_spawn(entry: Dictionary) -> void: func receive_peer_despawn(peer_id: int) -> void: if state != State.JOINED_CLIENT: return + _operator_peer_ids.erase(peer_id) _registry.remove_peer(peer_id) _spawn_service.remove_peer(peer_id) peer_removed.emit(peer_id) _emit_peer_count() +@rpc("authority", "call_remote", "reliable", 0) +func receive_operator_snapshot(peer_ids: PackedInt32Array) -> void: + if ( + state != State.JOINED_CLIENT + or peer_ids.size() > get_session_max_players() + ): + return + _operator_peer_ids.clear() + for peer_id: int in peer_ids: + if peer_id > 1: + _operator_peer_ids[peer_id] = true + _update_local_operator() + for peer_id: int in _operator_peer_ids: + operator_status_changed.emit(peer_id, true) + + +@rpc("authority", "call_remote", "reliable", 0) +func receive_operator_status(peer_id: int, enabled: bool) -> void: + if state != State.JOINED_CLIENT or peer_id <= 1: + return + if enabled: + _operator_peer_ids[peer_id] = true + else: + _operator_peer_ids.erase(peer_id) + _update_local_operator() + operator_status_changed.emit(peer_id, enabled) + + func _apply_spawn_entry(entry: Dictionary) -> void: if ( typeof(entry.get("peer_id")) != TYPE_INT @@ -1280,6 +1568,36 @@ func _sanitized_capabilities(value: Variant) -> PackedStringArray: return result +func _operator_for_fingerprint(fingerprint: String) -> bool: + return bool(( + _configured_operator_fingerprints + if _dedicated_host + else _session_operator_fingerprints + ).get(fingerprint, false)) + + +func _operator_peer_id_snapshot() -> PackedInt32Array: + var result: PackedInt32Array = PackedInt32Array() + for peer_id: int in _operator_peer_ids: + result.append(peer_id) + result.sort() + return result + + +func _set_operator_status(peer_id: int, enabled: bool) -> void: + if enabled: + _operator_peer_ids[peer_id] = true + else: + _operator_peer_ids.erase(peer_id) + operator_status_changed.emit(peer_id, enabled) + receive_operator_status.rpc(peer_id, enabled) + + +func _update_local_operator() -> void: + var local_peer_id: int = multiplayer.get_unique_id() + _local_operator = bool(_operator_peer_ids.get(local_peer_id, false)) + + func _verify_spawn_identity(entry: Dictionary) -> bool: var fingerprint := str(entry.get("identity_fingerprint", "")) var public_pem := NetworkIdentityCrypto.normalize_public_pem( @@ -1324,6 +1642,8 @@ func _verify_spawn_identity(entry: Dictionary) -> bool: func _identity_proof_fields(data: Dictionary) -> Array: return [ NetworkProtocol.PROTOCOL_VERSION, + str(data.get("client_game_version", "")), + str(data.get("server_game_version", "")), str(data.get("attempt_id", "")), str(data.get("client_fingerprint", "")), str(data.get("client_nonce", "")), @@ -1364,6 +1684,10 @@ func _valid_server_proof_shape(data: Variant) -> bool: and str(value["server_nonce"]).length() == 64 and NetworkIdentityCrypto.valid_fingerprint(value.get("client_fingerprint")) and NetworkIdentityCrypto.valid_fingerprint(value.get("server_fingerprint")) + and typeof(value.get("client_game_version")) == TYPE_STRING + and str(value["client_game_version"]) == NetworkProtocol.game_version() + and typeof(value.get("server_game_version")) == TYPE_STRING + and str(value["server_game_version"]) == NetworkProtocol.game_version() and typeof(value.get("server_public_key")) == TYPE_STRING and str(value["server_public_key"]).to_utf8_buffer().size() <= NetworkProtocol.MAX_PUBLIC_KEY_LENGTH @@ -1417,17 +1741,29 @@ func _is_valid_movement_input(data: Dictionary) -> bool: or typeof(data.get("sprint")) != TYPE_BOOL or typeof(data.get("sneak")) != TYPE_BOOL or typeof(data.get("slow_walk")) != TYPE_BOOL + or typeof(data.get("sitting")) != TYPE_BOOL + or typeof(data.get("casting")) != TYPE_BOOL + or not NetworkPlayerAnimationProtocol.validate_action_state( + data.get("animation_action") + ) or typeof(data.get("camera_yaw")) not in [TYPE_FLOAT, TYPE_INT] ): return false var axis: Array = data["axis"] if axis.size() != 2: return false + if ( + typeof(axis[0]) not in [TYPE_FLOAT, TYPE_INT] + or typeof(axis[1]) not in [TYPE_FLOAT, TYPE_INT] + ): + return false var x: float = float(axis[0]) var y: float = float(axis[1]) var camera_yaw: float = float(data["camera_yaw"]) return ( - is_finite(x) + int(data["sequence"]) > 0 + and int(data["sequence"]) <= MAX_MOVEMENT_INPUT_SEQUENCE + and is_finite(x) and is_finite(y) and is_finite(camera_yaw) and absf(x) <= 1.01 @@ -1729,3 +2065,8 @@ func _teardown_peer() -> void: _server_identity_fingerprint = "" _server_identity_public_key = "" _session_identity_keys.clear() + _session_operator_fingerprints.clear() + _operator_peer_ids.clear() + _local_operator = false + _host_port = 0 + _dedicated_host = false diff --git a/network/network_shop_protocol.gd b/network/network_shop_protocol.gd index 65a0395..11900e9 100644 --- a/network/network_shop_protocol.gd +++ b/network/network_shop_protocol.gd @@ -56,6 +56,11 @@ static func validate_request(data: Variant) -> String: var quantity: int = payload["quantity"] var wallet_balance: int = payload["wallet_balance"] var current_state: int = payload["current_state"] + if ( + payload.has("bait_unlocked") + and typeof(payload["bait_unlocked"]) != TYPE_BOOL + ): + return "Purchase could not be completed." if ( request_id.is_empty() or request_id.length() > MAX_ID_LENGTH diff --git a/network/network_shop_service.gd b/network/network_shop_service.gd index cffcee5..4dd0f98 100644 --- a/network/network_shop_service.gd +++ b/network/network_shop_service.gd @@ -108,11 +108,21 @@ func is_local_purchase_pending() -> bool: func request_supply(item_id: StringName) -> String: + var owned: int = _bag.get_quantity(item_id) if _bag != null else 0 + var item: ItemDataType = ( + _item_catalog.get_item_by_id(item_id) + if _item_catalog != null else null + ) + var quantity: int = FishingShopStockType.get_purchase_quantity( + item_id, + owned, + item.max_stack if item != null else FishingShopStockType.WORM_MAX_STACK, + ) return _request_purchase( item_id, NetworkShopProtocol.ProductCategory.SUPPLY, - 1, - _bag.get_quantity(item_id) if _bag != null else 0 + quantity, + owned ) @@ -221,6 +231,13 @@ func _request_purchase( "wallet_balance": _wallet.get_balance() if _wallet != null else 0, "current_state": current_state, } + if ( + category == NetworkShopProtocol.ProductCategory.SUPPLY + and FishingShopStockType.is_bait_topoff(product_id) + ): + request["bait_unlocked"] = ( + _bag != null and _bag.is_bait_unlocked(product_id) + ) _pending_local_request = request.duplicate(true) local_purchase_pending.emit(request_id) if _session.is_host(): @@ -307,7 +324,7 @@ func _build_authoritative_result( var rejection: String = "" if StringName(str(request["shop_id"])) != SHOP_ID: rejection = "The shop is unavailable." - elif quantity != 1: + elif quantity < 1: rejection = "Purchase could not be completed." else: match category: @@ -315,17 +332,34 @@ func _build_authoritative_result( var item: ItemDataType = _item_catalog.get_item_by_id( product_id ) if _item_catalog != null else null - cost = FishingShopStockType.get_price(product_id) + var bait_topoff: bool = ( + FishingShopStockType.is_bait_topoff(product_id) + ) + var permanent_unlock: bool = ( + FishingShopStockType.is_permanent_unlock(product_id, item) + ) if ( item == null or not item.is_valid() or product_id not in ( FishingShopStockType.get_stock_item_ids() ) - or item.category != ItemDataType.Category.CONSUMABLE - or not item.stackable - or not item.usable + or ( + item.category != ItemDataType.Category.CONSUMABLE + and not bait_topoff + and not permanent_unlock + ) + or (not item.stackable and not permanent_unlock) + or ( + not item.usable + and not bait_topoff + and not permanent_unlock + and not FishingShopStockType.is_passive_supply( + product_id + ) + ) or current_state >= item.max_stack + or current_state + quantity > item.max_stack ): rejection = ( "Your Bag is full." @@ -333,7 +367,22 @@ func _build_authoritative_result( else "Purchase could not be completed." ) else: - resulting_state = current_state + 1 + var expected_quantity: int = ( + FishingShopStockType.get_purchase_quantity( + product_id, current_state, item.max_stack + ) + ) + if quantity != expected_quantity: + rejection = "Purchase could not be completed." + else: + cost = FishingShopStockType.get_purchase_cost( + product_id, + quantity, + bool(request.get("bait_unlocked", true)), + ) + if cost < 0: + rejection = "Purchase could not be completed." + resulting_state = current_state + quantity NetworkShopProtocol.ProductCategory.ROD: rejection = "This item is not sold here." NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE: @@ -415,7 +464,7 @@ func _build_authoritative_result( current_state, product_id ) if rejection.is_empty() and (cost < 0 or wallet_balance < cost): - rejection = "Not enough fish coin." + rejection = "Insufficient funds." if not rejection.is_empty(): return _rejected_result(request, rejection) return { @@ -525,6 +574,9 @@ func _apply_purchase_result(data: Dictionary) -> void: return var wallet_snapshot: int = _wallet.get_balance() var bag_snapshot: Array[OwnedItemType] = _bag.get_all_items() + var bait_unlock_snapshot: Array[StringName] = ( + _bag.get_unlocked_bait_ids() + ) var reel_snapshot: int = _upgrades.get_reel_speed_level() var barrier_snapshot: int = _upgrades.get_barrier_power_level() var cooler_snapshot: int = _cooler_capacity.get_level() @@ -532,6 +584,7 @@ func _apply_purchase_result(data: Dictionary) -> void: var applied: bool = _apply_local_product(data) if not applied or not _save_manager.save_if_dirty(): _bag.replace_all_items(bag_snapshot) + _bag.replace_unlocked_bait_ids(bait_unlock_snapshot) _upgrades.restore_levels(reel_snapshot, barrier_snapshot) _cooler_capacity.restore_level(cooler_snapshot) _art_unlocks.restore_mask(art_snapshot) @@ -576,14 +629,29 @@ func _validate_local_result(data: Dictionary) -> String: var resulting_state: int = int(data["resulting_state"]) if ( category != NetworkShopProtocol.ProductCategory.ART_UPGRADE - and resulting_state != expected_state + 1 + and resulting_state != expected_state + int(data["quantity"]) ): return "Purchase could not be completed." if not _wallet.can_afford(cost): - return "Not enough fish coin." + return "Insufficient funds." match category: NetworkShopProtocol.ProductCategory.SUPPLY: - if _bag.get_quantity(product_id) != expected_state: + var item: ItemDataType = ( + _item_catalog.get_item_by_id(product_id) + if _item_catalog != null else null + ) + var bait_unlocked: bool = _bag.is_bait_unlocked(product_id) + if ( + item == null + or _bag.get_quantity(product_id) != expected_state + or int(data["quantity"]) != FishingShopStockType.get_purchase_quantity( + product_id, expected_state, item.max_stack + ) + ): + return "Purchase could not be completed." + if cost != FishingShopStockType.get_purchase_cost( + product_id, int(data["quantity"]), bait_unlocked + ): return "Purchase could not be completed." if not _bag.can_add_item(product_id, data["quantity"]): return "Your Bag is full." diff --git a/network/network_surface_drawing_service.gd b/network/network_surface_drawing_service.gd index 35c28ab..e42519c 100644 --- a/network/network_surface_drawing_service.gd +++ b/network/network_surface_drawing_service.gd @@ -27,6 +27,7 @@ const MAX_REQUESTS_PER_WINDOW: int = 32 const MAX_RECENT_REQUEST_IDS: int = 64 const GRID_PREVIEW_SURFACE_OFFSET: float = 0.03 const POINTER_EDGE_MARGIN: float = 2.0 +const INVALID_POINTER_SCREEN_POSITION := Vector2(-1.0, -1.0) enum GuideAction { NONE, @@ -40,6 +41,7 @@ var _spawn_service: PlayerSpawnService var _relationships: PlayerRelationshipStore var _local_player: Player var _bag: PlayerBag +var _hotbar: PlayerHotbar var _art_unlocks: PlayerArtUnlocks var _drawing_root: Node3D var _canvas_states: Dictionary[String, Dictionary] = {} @@ -86,6 +88,7 @@ func setup( local_player: Player, drawing_root: Node3D, bag: PlayerBag, + hotbar: PlayerHotbar, art_unlocks: PlayerArtUnlocks, ) -> void: _session = session @@ -94,6 +97,7 @@ func setup( _local_player = local_player _drawing_root = drawing_root _bag = bag + _hotbar = hotbar _art_unlocks = art_unlocks _refresh_local_unlocks() if _session != null: @@ -117,21 +121,11 @@ func setup( _emit_hud_state("") -func handle_input(event: InputEvent, can_open: bool) -> bool: - if event is InputEventKey: - var key_event := event as InputEventKey - if ( - key_event.pressed - and not key_event.echo - and key_event.physical_keycode == KEY_P - ): - if _active: - deactivate() - return true - if can_open and can_activate(): - activate() - return true - return false +func handle_input( + event: InputEvent, + can_open: bool, + pointer_screen_position: Vector2 = INVALID_POINTER_SCREEN_POSITION, +) -> bool: if not _active: return false if not can_open: @@ -144,8 +138,7 @@ func handle_input(event: InputEvent, can_open: bool) -> bool: if _placing_grid: _set_placement_mode(false) return true - deactivate() - return true + return false if event is InputEventKey: var key_event := event as InputEventKey if not key_event.pressed or key_event.echo: @@ -167,23 +160,19 @@ func handle_input(event: InputEvent, can_open: bool) -> bool: return true if event is InputEventMouseButton: var mouse_event := event as InputEventMouseButton - if ( - mouse_event.shift_pressed - and mouse_event.button_index in [ - MOUSE_BUTTON_WHEEL_UP, - MOUSE_BUTTON_WHEEL_DOWN, - ] - ): + # Ordinary wheel input always belongs to Hotbar selection. Brush size is + # selected explicitly in the Art Kit toolbar. + if mouse_event.button_index in [ + MOUSE_BUTTON_WHEEL_UP, + MOUSE_BUTTON_WHEEL_DOWN, + ]: return false + _set_pointer_from_input( + mouse_event.position, + pointer_screen_position, + ) + _update_aim() match mouse_event.button_index: - MOUSE_BUTTON_WHEEL_UP: - if mouse_event.pressed and not _placing_grid: - _set_brush_size(_brush_size + 1) - return true - MOUSE_BUTTON_WHEEL_DOWN: - if mouse_event.pressed and not _placing_grid: - _set_brush_size(_brush_size - 1) - return true MOUSE_BUTTON_LEFT: if _placing_grid: if mouse_event.pressed: @@ -216,32 +205,38 @@ func handle_input(event: InputEvent, can_open: bool) -> bool: var motion_event := event as InputEventMouseMotion if _camera_look_active: return false - _pointer_screen_position = _clamped_pointer_position( - motion_event.position + _set_pointer_from_input( + motion_event.position, + pointer_screen_position, ) _update_aim() return true return false -func activate() -> void: +func activate( + pointer_screen_position: Vector2 = INVALID_POINTER_SCREEN_POSITION, +) -> void: if _active or not can_activate(): return _active = true - _placing_grid = true + _placing_grid = false _eraser_mode = false _clear_armed_guide_action(false) _refresh_local_unlocks() _rebuild_placement_preview() _reset_stroke() - _pointer_screen_position = get_viewport().get_visible_rect().size * 0.5 + var initial_pointer_position: Vector2 = pointer_screen_position + if not _is_valid_pointer_screen_position(initial_pointer_position): + initial_pointer_position = get_viewport().get_mouse_position() + _pointer_screen_position = _clamped_pointer_position( + initial_pointer_position + ) _prior_mouse_mode = Input.mouse_mode Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _update_aim() _refresh_stencil_visibility() - _emit_hud_state( - "click to place a shared grid" - ) + _emit_hud_state("marker selected") func deactivate() -> void: @@ -270,7 +265,11 @@ func is_placement_mode() -> bool: func can_activate() -> bool: - return _drawing_available() and _owns_art_kit() + return ( + _drawing_available() + and _owns_art_kit() + and _art_kit_is_selected() + ) func set_placement_mode(enabled: bool) -> void: @@ -326,7 +325,7 @@ func arm_guide_action(action: int) -> bool: _selected_canvas_id = "" _update_aim() _refresh_stencil_visibility() - _emit_hud_state("click a grid to %s it" % _guide_action_verb(action)) + _emit_hud_state(_guide_action_status(action)) return true @@ -397,6 +396,15 @@ func _owns_art_kit() -> bool: return _bag != null and _bag.owns_item(ArtShopStockType.ART_KIT_ITEM_ID) +func _art_kit_is_selected() -> bool: + return ( + _hotbar != null + and _hotbar.get_selected_assignment_kind() + == PlayerHotbar.AssignmentKind.ITEM + and _hotbar.get_selected_item_id() == ArtShopStockType.ART_KIT_ITEM_ID + ) + + func _local_entitlement() -> Dictionary: return { "has_kit": _owns_art_kit(), @@ -532,8 +540,7 @@ func request_canvas_at_surface( tangent: Vector3, ) -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or _art_unlocks == null or not _art_unlocks.is_grid_size_unlocked(_grid_size) or normal.is_zero_approx() @@ -563,8 +570,7 @@ func request_cell_edits( stroke_id: String = "", ) -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or canvas_id.is_empty() or edits.is_empty() ): @@ -595,7 +601,7 @@ func request_guide_visibility( should_be_visible: bool, should_finalize: bool = false, ) -> bool: - if not _drawing_available() or not _owns_art_kit() or canvas_id.is_empty(): + if not can_activate() or canvas_id.is_empty(): return false var data: Dictionary = { "request_id": _new_request_id("guide"), @@ -615,8 +621,7 @@ func request_guide_visibility( func request_undo_last_stroke() -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or _last_local_stroke_id.is_empty() ): _emit_hud_state("nothing to undo") @@ -680,6 +685,9 @@ func get_canvas_state(canvas_id: String) -> Dictionary: func _process(_delta: float) -> void: if not _active: return + if not can_activate(): + deactivate() + return _update_aim() if _placing_grid: return @@ -734,7 +742,7 @@ func _update_aim() -> void: func _raycast_from_pointer() -> Dictionary: if _local_player == null or not is_instance_valid(_local_player): return {} - var camera: Camera3D = _local_player.get_gameplay_camera() + var camera: Camera3D = _local_player.get_active_gameplay_camera() if camera == null or not camera.current: return {} var from: Vector3 = camera.project_ray_origin(_pointer_screen_position) @@ -1666,7 +1674,9 @@ func _is_static_surface(value: Variant) -> bool: func _surface_tangent(normal: Vector3) -> Vector3: var camera: Camera3D = ( - _local_player.get_gameplay_camera() if _local_player != null else null + _local_player.get_active_gameplay_camera() + if _local_player != null + else null ) var candidate: Vector3 = ( camera.global_basis.x if camera != null else Vector3.RIGHT @@ -1854,8 +1864,10 @@ func _execute_armed_guide_action() -> void: _restore_selected_guide() GuideAction.FINALIZE: _finalize_selected_guide() - _clear_armed_guide_action(true) - _emit_hud_state("") + # Guide actions are persistent modes. Keep the selected action armed so + # multiple grids can be handled without returning to the toolbar after + # every click; selecting the active toggle again explicitly exits it. + _emit_hud_state(_guide_action_status(action)) func _cancel_armed_guide_action() -> void: @@ -1884,12 +1896,18 @@ func _guide_action_verb(action: int) -> String: GuideAction.HIDE: return "hide" GuideAction.RESTORE: - return "restore" + return "show" GuideAction.FINALIZE: return "finish" return "use" +func _guide_action_status(action: int) -> String: + return "%s grid mode • click grids • toggle again to exit" % ( + _guide_action_verb(action).capitalize() + ) + + func _remove_selected_guide() -> void: if _selected_canvas_id.is_empty(): _emit_hud_state("aim at a placed grid before removing its guide") @@ -1940,6 +1958,25 @@ func _clamped_pointer_position(value: Vector2) -> Vector2: ) +func _set_pointer_from_input( + event_position: Vector2, + pointer_screen_position: Vector2, +) -> void: + # GameUI receives input through a scaled SubViewport, so event_position is + # expressed in that UI viewport's render coordinates. Camera3D ray methods + # require coordinates in the camera's root viewport instead. GameUI supplies + # that authoritative window position; direct callers may still use an event + # position when they share this service's viewport. + var resolved_position: Vector2 = pointer_screen_position + if not _is_valid_pointer_screen_position(resolved_position): + resolved_position = event_position + _pointer_screen_position = _clamped_pointer_position(resolved_position) + + +func _is_valid_pointer_screen_position(value: Vector2) -> bool: + return value.x >= 0.0 and value.y >= 0.0 + + func _cycle_color(direction: int) -> void: if _color_ids.is_empty(): return @@ -2124,7 +2161,7 @@ func _on_peer_removed(peer_id: int) -> void: func _on_local_art_entitlement_changed() -> void: - if _active and not _owns_art_kit(): + if _active and not can_activate(): deactivate() _publish_local_entitlement() diff --git a/network/network_transport.gd b/network/network_transport.gd index 8b0cd05..ce68172 100644 --- a/network/network_transport.gd +++ b/network/network_transport.gd @@ -1,6 +1,7 @@ class_name NetworkTransport extends Node +@warning_ignore("unused_signal") signal transport_error(message: String) var _peer: MultiplayerPeer diff --git a/network/network_world_time_service.gd b/network/network_world_time_service.gd index 91b4060..246af77 100644 --- a/network/network_world_time_service.gd +++ b/network/network_world_time_service.gd @@ -17,6 +17,12 @@ func setup(session: NetworkSession, world_time: WorldTimeService) -> void: _world_time = world_time _session.state_changed.connect(_on_session_state_changed) _session.peer_authenticated.connect(_on_peer_authenticated) + if not _world_time.authoritative_time_set.is_connected( + _on_authoritative_time_set + ): + _world_time.authoritative_time_set.connect( + _on_authoritative_time_set + ) set_process(true) @@ -82,6 +88,13 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void: _send_snapshot(peer_id) +func _on_authoritative_time_set(_time_hours: float) -> void: + if _session == null or not _session.is_host(): + return + _sync_elapsed = 0.0 + _broadcast_snapshot() + + func _broadcast_snapshot() -> void: if _session == null or not _session.is_host(): return diff --git a/network/network_world_weather_service.gd b/network/network_world_weather_service.gd index ec0cab7..43d5762 100644 --- a/network/network_world_weather_service.gd +++ b/network/network_world_weather_service.gd @@ -44,6 +44,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void: _sequence = 0 _last_received_sequence = -1 _sync_elapsed = 0.0 + _world_weather.set_persistence_tracking_enabled(true) _world_weather.begin_authoritative_session( _active_session_id.hash() ) @@ -52,6 +53,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void: _active_session_id = _session.get_session_id() _last_received_sequence = -1 _sync_elapsed = 0.0 + _world_weather.set_persistence_tracking_enabled(false) if _session.supports_server_capability( NetworkProtocol.WORLD_WEATHER_CAPABILITY ): @@ -69,6 +71,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void: _sequence = 0 _last_received_sequence = -1 _sync_elapsed = 0.0 + _world_weather.set_persistence_tracking_enabled(false) _world_weather.end_session() diff --git a/network/player_data_root.gd b/network/player_data_root.gd index c0fe26e..525c545 100644 --- a/network/player_data_root.gd +++ b/network/player_data_root.gd @@ -72,7 +72,17 @@ func resolve() -> bool: func default_visible_path() -> String: var documents: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - return documents.path_join("NETFISHING") if not documents.is_empty() else "" + if documents.is_empty(): + return "" + var candidate: String = _normalize(documents.path_join("NETFISHING")) + if not candidate.is_absolute_path(): + return "" + var classification: StringName = classify_candidate_path( + candidate, + ProjectSettings.globalize_path("res://"), + OS.get_executable_path().get_base_dir(), + ) + return candidate if classification == PATH_ALLOWED else "" func select_new_root(path: String, app_data: bool = false) -> bool: @@ -159,6 +169,13 @@ func use_existing_root(path: String) -> bool: return true +func activate_process_root(path: String) -> bool: + _load_bootstrap_identity() + override_active = true + mode = Mode.COMMAND_LINE_OVERRIDE + return _activate_existing(path, "", false) + + func path_for(store_owner: StringName) -> String: var relative: String = { &"player_save": "player/player_save.json", @@ -232,14 +249,21 @@ func _validate_candidate(path: String, create: bool) -> bool: var normalized: String = _normalize(path) if normalized.is_empty() or not normalized.is_absolute_path(): return _fail("Choose an absolute filesystem folder.") + var app_data_path: String = _normalize( + ProjectSettings.globalize_path(APP_DATA_PORTABLE_PATH) + ) + var exported_app_data: bool = ( + normalized == app_data_path + and not OS.has_feature("editor") + ) var classification: StringName = classify_candidate_path( normalized, ProjectSettings.globalize_path("res://"), OS.get_executable_path().get_base_dir(), ) - if classification == PATH_SOURCE_PROJECT: + if classification == PATH_SOURCE_PROJECT and not exported_app_data: return _fail("The project folder cannot be used as the player data folder.") - if classification == PATH_INSTALLATION: + if classification == PATH_INSTALLATION and not exported_app_data: return _fail( "The application installation folder cannot be used as the player data folder." ) diff --git a/network/player_list_entry.gd b/network/player_list_entry.gd index fbe0d8f..f401da6 100644 --- a/network/player_list_entry.gd +++ b/network/player_list_entry.gd @@ -6,6 +6,7 @@ var full_fingerprint := "" var compact_fingerprint := "" var display_name := "" var is_host := false +var is_operator := false var is_local_player := false var continuity_state := "" var ping_to_host_ms := -1 @@ -13,4 +14,5 @@ var muted := false var blocked := false var can_kick := false var can_ban := false +var can_manage_operator := false var revision := 0 diff --git a/player/animalese_voice_profiles.gd b/player/animalese_voice_profiles.gd new file mode 100644 index 0000000..2ea6cab --- /dev/null +++ b/player/animalese_voice_profiles.gd @@ -0,0 +1,81 @@ +class_name AnimaleseVoiceProfiles +extends RefCounted + +const DEFAULT_ID: String = "natural" +const DEFAULT_SPEED_ID: String = "normal" +const DEFAULT_CALL_ID: String = "meow" +const CALL_AUDIO_DIRECTORY: String = "res://sound/dialogue/calls" +const OPTIONS: Array[Dictionary] = [ + {"id": "tiny", "label": "tiny", "pitch": 1.24}, + {"id": "bright", "label": "bright", "pitch": 1.10}, + {"id": "natural", "label": "natural", "pitch": 1.0}, + {"id": "mellow", "label": "mellow", "pitch": 0.89}, + {"id": "deep", "label": "deep", "pitch": 0.77}, +] +const SPEED_OPTIONS: Array[Dictionary] = [ + {"id": "slow", "label": "slow", "characters_per_second": 20.0}, + {"id": "relaxed", "label": "relaxed", "characters_per_second": 24.0}, + {"id": "normal", "label": "normal", "characters_per_second": 28.0}, + {"id": "quick", "label": "quick", "characters_per_second": 34.0}, + {"id": "rapid", "label": "rapid", "characters_per_second": 40.0}, +] +const CALL_OPTIONS: Array[Dictionary] = [ + {"id": "meow", "label": "meow"}, + {"id": "bark", "label": "bark"}, +] + + +static func is_valid(voice_id: String) -> bool: + for option: Dictionary in OPTIONS: + if str(option.get("id", "")) == voice_id: + return true + return false + + +static func sanitized_id(voice_id: String) -> String: + return voice_id if is_valid(voice_id) else DEFAULT_ID + + +static func pitch_for(voice_id: String) -> float: + var resolved_id := sanitized_id(voice_id) + for option: Dictionary in OPTIONS: + if str(option.get("id", "")) == resolved_id: + return float(option.get("pitch", 1.0)) + return 1.0 + + +static func is_valid_speed(speed_id: String) -> bool: + for option: Dictionary in SPEED_OPTIONS: + if str(option.get("id", "")) == speed_id: + return true + return false + + +static func sanitized_speed_id(speed_id: String) -> String: + return speed_id if is_valid_speed(speed_id) else DEFAULT_SPEED_ID + + +static func speed_for(speed_id: String) -> float: + var resolved_id := sanitized_speed_id(speed_id) + for option: Dictionary in SPEED_OPTIONS: + if str(option.get("id", "")) == resolved_id: + return float(option.get("characters_per_second", 28.0)) + return 28.0 + + +static func is_valid_call(call_id: String) -> bool: + for option: Dictionary in CALL_OPTIONS: + if str(option.get("id", "")) == call_id: + return true + return false + + +static func sanitized_call_id(call_id: String) -> String: + return call_id if is_valid_call(call_id) else DEFAULT_CALL_ID + + +static func call_audio_path(call_id: String) -> String: + return "%s/%s.wav" % [ + CALL_AUDIO_DIRECTORY, + sanitized_call_id(call_id), + ] diff --git a/player/animalese_voice_profiles.gd.uid b/player/animalese_voice_profiles.gd.uid new file mode 100644 index 0000000..1817007 --- /dev/null +++ b/player/animalese_voice_profiles.gd.uid @@ -0,0 +1 @@ +uid://dlut32tq2v66j diff --git a/player/fishing_rod_attachment.tscn b/player/fishing_rod_attachment.tscn index 4771285..46da419 100644 --- a/player/fishing_rod_attachment.tscn +++ b/player/fishing_rod_attachment.tscn @@ -10,15 +10,14 @@ albedo_color = Color(0.22, 0.12, 0.06, 1) roughness = 0.8 [node name="FishingRodAttachment" type="BoneAttachment3D"] -bone_name = "hand.R" +bone_name = "rod_socket" [node name="FishingRod" type="Node3D" parent="."] -rotation = Vector3(-0.785398, -0.007863, 3.141593) [node name="RodMesh" type="MeshInstance3D" parent="FishingRod"] -position = Vector3(0, 0.42, 0) +position = Vector3(0, 0.5, 0) mesh = SubResource("FishingRodMesh") material_override = SubResource("FishingRodMaterial") [node name="FishingRodTip" type="Marker3D" parent="FishingRod"] -position = Vector3(0, 0.95, 0) +position = Vector3(0, 1.1, 0) diff --git a/player/fur_pattern.gdshader b/player/fur_pattern.gdshader new file mode 100644 index 0000000..aad013f --- /dev/null +++ b/player/fur_pattern.gdshader @@ -0,0 +1,27 @@ +shader_type spatial; +render_mode unshaded, cull_back; + +// Pattern PNGs are data maps rather than colored artwork. Black selects the +// base color and the red, green, and blue channels select the three authored +// accent colors. Alpha is intentionally ignored so artists may leave unused +// UV space transparent. +uniform sampler2D pattern_texture : filter_nearest, repeat_disable; +uniform vec4 base_color : source_color = vec4(1.0); +uniform vec4 red_zone_color : source_color = vec4(0.9, 0.8, 0.6, 1.0); +uniform vec4 green_zone_color : source_color = vec4(0.4, 0.2, 0.1, 1.0); +uniform vec4 blue_zone_color : source_color = vec4(0.7, 0.2, 0.15, 1.0); + +void fragment() { + vec3 zone_weights = max(texture(pattern_texture, UV).rgb, vec3(0.0)); + float zone_total = zone_weights.r + zone_weights.g + zone_weights.b; + // Filtering between two authored zone colors can produce a sum near one. + // Normalize only an overfull sample, retaining smooth black-to-zone edges. + zone_weights /= max(zone_total, 1.0); + float base_weight = 1.0 - min(zone_total, 1.0); + ALBEDO = ( + base_color.rgb * base_weight + + red_zone_color.rgb * zone_weights.r + + green_zone_color.rgb * zone_weights.g + + blue_zone_color.rgb * zone_weights.b + ); +} diff --git a/player/fur_pattern.gdshader.uid b/player/fur_pattern.gdshader.uid new file mode 100644 index 0000000..e16692e --- /dev/null +++ b/player/fur_pattern.gdshader.uid @@ -0,0 +1 @@ +uid://eoeegcj00qy8 diff --git a/player/held_item_attachment.tscn b/player/held_item_attachment.tscn new file mode 100644 index 0000000..66b3b7c --- /dev/null +++ b/player/held_item_attachment.tscn @@ -0,0 +1,44 @@ +[gd_scene format=3] + +[node name="HeldItemAttachment" type="BoneAttachment3D"] +bone_name = "hand.R" + +[node name="HeldFishDisplay" type="Node3D" parent="."] +unique_name_in_owner = true +position = Vector3(0, 0.08, 0.04) +top_level = true +visible = false + +[node name="HeldFishSprite" type="Sprite3D" parent="HeldFishDisplay"] +unique_name_in_owner = true +pixel_size = 0.012 +shaded = false +double_sided = true +texture_filter = 0 + +[node name="HeldArtKitDisplay" type="Node3D" parent="."] +unique_name_in_owner = true +position = Vector3(0, 0.08, 0.04) +visible = false + +[node name="HeldArtKitSprite" type="Sprite3D" parent="HeldArtKitDisplay"] +unique_name_in_owner = true +rotation = Vector3(0, 0, 4.71239) +pixel_size = 0.008 +flip_h = true +shaded = false +double_sided = true +texture_filter = 0 + +[node name="CatchDisplay" type="Node3D" parent="."] +unique_name_in_owner = true +position = Vector3(0, 0.08, 0.04) +top_level = true +visible = false + +[node name="CatchSprite" type="Sprite3D" parent="CatchDisplay"] +unique_name_in_owner = true +pixel_size = 0.012 +shaded = false +double_sided = true +texture_filter = 0 diff --git a/player/player.gd b/player/player.gd index 2f0f134..6284873 100644 --- a/player/player.gd +++ b/player/player.gd @@ -8,6 +8,7 @@ const FishDataType = preload("res://fish/fish_data.gd") const FishSaleServiceType = preload("res://economy/fish_sale_service.gd") const PlayerWalletType = preload("res://economy/player_wallet.gd") const PlayerBagType = preload("res://inventory/player_bag.gd") +const ItemDataType = preload("res://items/item_data.gd") const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") const PlayerFishingUpgradesType = preload( "res://progression/player_fishing_upgrades.gd" @@ -24,23 +25,254 @@ const PlayerArtUnlocksType = preload( const PlayerExperienceType = preload( "res://progression/player_experience.gd" ) +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) const FishingRodAttachmentScene = preload( "res://player/fishing_rod_attachment.tscn" ) +const HeldItemAttachmentScene = preload( + "res://player/held_item_attachment.tscn" +) +const SprintDustTrailType = preload("res://player/sprint_dust_trail.gd") + +signal retract_visual_finished const CHARACTER_IDLE_ANIMATION: StringName = &"idle" +const CHARACTER_IDLE_SHOW_ANIMATION: StringName = &"idle_show" +const CHARACTER_IDLE_SIT_ANIMATION: StringName = &"idle_sit" +const CHARACTER_IDLE_SIT_SHOW_ANIMATION: StringName = &"idle_sit_show" const CHARACTER_WALKING_ANIMATION: StringName = &"walking" -const CHARACTER_SITTING_ANIMATION: StringName = &"sitting" +const CHARACTER_WALKING_SHOW_ANIMATION: StringName = &"walking_show" +const CHARACTER_RUNNING_ANIMATION: StringName = &"running" +const CHARACTER_RUNNING_SHOW_ANIMATION: StringName = &"running_show" +const CHARACTER_CASTING_ANIMATION: StringName = &"casting" +const CHARACTER_CASTING_SIT_ANIMATION: StringName = &"casting_sit" +const CHARACTER_RELEASE_ANIMATION: StringName = &"release" +const CHARACTER_RELEASE_SIT_ANIMATION: StringName = &"release_sit" +const CHARACTER_RETRACT_ANIMATION: StringName = &"retract" +const CHARACTER_RETRACT_SIT_ANIMATION: StringName = &"retract_sit" +const CHARACTER_POCKET_IDLE_IDLE_ANIMATION: StringName = &"pocket_idle_idle" +const CHARACTER_POCKET_IDLE_SHOW_ANIMATION: StringName = &"pocket_idle_show" +const CHARACTER_POCKET_SHOW_SHOW_ANIMATION: StringName = &"pocket_show_show" +const CHARACTER_POCKET_SHOW_IDLE_ANIMATION: StringName = &"pocket_show_idle" +const CHARACTER_POCKET_SIT_IDLE_IDLE_ANIMATION: StringName = &"pocket_sit_idle_idle" +const CHARACTER_POCKET_SIT_IDLE_SHOW_ANIMATION: StringName = &"pocket_sit_idle_show" +const CHARACTER_POCKET_SIT_SHOW_SHOW_ANIMATION: StringName = &"pocket_sit_show_show" +const CHARACTER_POCKET_SIT_SHOW_IDLE_ANIMATION: StringName = &"pocket_sit_show_idle" +const CHARACTER_POCKET_WALKING_IDLE_IDLE_ANIMATION: StringName = &"pocket_walking_idle_idle" +const CHARACTER_POCKET_WALKING_IDLE_SHOW_ANIMATION: StringName = &"pocket_walking_idle_show" +const CHARACTER_POCKET_WALKING_SHOW_SHOW_ANIMATION: StringName = &"pocket_walking_show_show" +const CHARACTER_POCKET_WALKING_SHOW_IDLE_ANIMATION: StringName = &"pocket_walking_show_idle" +const CHARACTER_FISHING_ANIMATION: StringName = &"fishing" +const CHARACTER_FISHING_SIT_ANIMATION: StringName = &"fishing_sit" +const CHARACTER_FIGHTING_ANIMATION: StringName = &"fighting" +const CHARACTER_FIGHTING_SIT_ANIMATION: StringName = &"fighting_sit" +# Add future networked emote animation IDs here. The protocol accepts unknown +# safe IDs so newer clients can extend it, but Player only presents actions +# explicitly approved by this catalog. +const NETWORK_ANIMATION_ACTION_IDS: Array[StringName] = [] + +enum FishingVisualPhase { + NONE, + CASTING, + RELEASE, + RETRACT, + FISHING, +} + +enum PocketVisualTarget { + NONE, + IDLE_ITEM, + HELD_FISH, + ART_KIT, + CATCH_SHOWCASE, +} + +enum LocomotionState { + IDLE, + WALKING, + RUNNING, +} +const FIGHTING_EYES_ID: String = "alligator_eyes" +const BLINK_EYES_ID: String = "closed" +const BLINK_INTERVAL_SECONDS := Vector2(2.8, 7.5) +const BLINK_DURATION_SECONDS: float = 0.11 +const CHARACTER_CALL_MOUTH_ID: String = "open_ah" +const CHARACTER_CALL_MOUTH_DURATION_SECONDS: float = 0.16 +const BASE_REEL_SPEED: float = 0.16 +const LANDING_DUST_MIN_FALL_SPEED: float = 2.5 +# The target Android handheld exposes its physical right trigger through +# Godot's left-trigger axis. Keep the role named here so the platform mapping +# remains isolated from camera behavior. +const CONTROLLER_ZOOM_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT var appearance_snapshot: Dictionary = ( CharacterCustomizationCatalog.default_snapshot() ) +var animalese_voice_id: String = "natural" +var active_bait_id: StringName = StringName() +var active_lure_id: StringName = StringName() +signal active_bait_changed(item_id: StringName) +signal active_lure_changed(item_id: StringName) + + +func equip_bait(item: ItemDataType) -> bool: + if item == null or not item.is_bait() or bag == null: + return false + if not bag.owns_item(item.item_id): + return false + active_bait_id = item.item_id + active_bait_changed.emit(active_bait_id) + return true + + +func unequip_bait() -> void: + if active_bait_id.is_empty(): + return + active_bait_id = StringName() + active_bait_changed.emit(active_bait_id) + + +func equip_lure(item: ItemDataType) -> bool: + if item == null or not item.is_lure() or bag == null: + return false + if not bag.owns_item(item.item_id): + return false + active_lure_id = item.item_id + active_lure_changed.emit(active_lure_id) + return true + + +func unequip_lure() -> void: + if active_lure_id.is_empty(): + return + active_lure_id = StringName() + active_lure_changed.emit(active_lure_id) func apply_appearance_snapshot(snapshot: Dictionary) -> void: if CharacterCustomizationCatalog.validate_snapshot(snapshot): appearance_snapshot = snapshot.duplicate(true) - PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot) + _apply_presented_appearance() + + +func get_character_visual_scale() -> float: + return CharacterCustomizationCatalog.character_scale( + appearance_snapshot.get( + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ) + + +func set_fighting_visual(active: bool) -> void: + if _fighting_visual_active == active: + return + _fighting_visual_active = active + if active: + _blink_visual_active = false + _schedule_next_blink() + _apply_presented_appearance() + _character_animation_name = &"" + _update_character_animation() + + +func play_character_call_visual() -> void: + _character_call_visual_generation += 1 + var generation: int = _character_call_visual_generation + _character_call_mouth_active = true + _apply_presented_appearance() + await get_tree().create_timer( + CHARACTER_CALL_MOUTH_DURATION_SECONDS + ).timeout + if generation != _character_call_visual_generation: + return + _character_call_mouth_active = false + _apply_presented_appearance() + + +func set_fishing_visual(active: bool) -> void: + if ( + active + and _fishing_visual_phase == FishingVisualPhase.RELEASE + and _character_animation_name in [ + CHARACTER_RELEASE_ANIMATION, + CHARACTER_RELEASE_SIT_ANIMATION, + ] + and _character_animation_player != null + and _character_animation_player.is_playing() + ): + _fishing_after_release_pending = true + return + _set_fishing_visual_phase( + FishingVisualPhase.FISHING if active else FishingVisualPhase.NONE + ) + + +func set_casting_visual() -> void: + _set_fishing_visual_phase(FishingVisualPhase.CASTING) + + +func set_release_visual() -> void: + _set_fishing_visual_phase(FishingVisualPhase.RELEASE) + + +func set_retract_visual() -> void: + _retract_animation_completed = false + _set_fishing_visual_phase(FishingVisualPhase.RETRACT) + if ( + _character_animation_player == null + or _character_animation_name not in [ + CHARACTER_RETRACT_ANIMATION, + CHARACTER_RETRACT_SIT_ANIMATION, + ] + or not _character_animation_player.is_playing() + ): + _complete_retract_animation() + + +func _set_fishing_visual_phase(phase: FishingVisualPhase) -> void: + if phase != FishingVisualPhase.NONE and _pocket_visual_active: + _complete_pocket_visual() + if _fishing_visual_phase == phase: + return + if ( + _fishing_visual_phase == FishingVisualPhase.RETRACT + and phase != FishingVisualPhase.RETRACT + ): + _complete_retract_animation() + if phase != FishingVisualPhase.RELEASE: + _fishing_after_release_pending = false + _fishing_visual_phase = phase + _fishing_visual_active = phase != FishingVisualPhase.NONE + _character_animation_name = &"" + _update_character_animation() + + +func _apply_presented_appearance() -> void: + if not is_node_ready() or _visuals == null: + return + var presented_appearance := appearance_snapshot + if _blink_visual_active and not _fighting_visual_active: + presented_appearance = appearance_snapshot.duplicate(true) + presented_appearance["eyes"] = BLINK_EYES_ID + if _fighting_visual_active: + presented_appearance = appearance_snapshot.duplicate(true) + presented_appearance["eyes"] = FIGHTING_EYES_ID + if _character_call_mouth_active: + if presented_appearance == appearance_snapshot: + presented_appearance = appearance_snapshot.duplicate(true) + presented_appearance["mouth"] = CHARACTER_CALL_MOUTH_ID + PlayerVisualPresenter.apply_appearance(_visuals, presented_appearance) + + +func apply_animalese_voice_id(voice_id: String) -> void: + animalese_voice_id = voice_id + + +func get_animalese_voice_id() -> String: + return animalese_voice_id class ShowcaseCameraSnapshot: extends RefCounted @@ -55,9 +287,9 @@ class ShowcaseCameraSnapshot: @export_category("Movement") -@export var walk_speed: float = 4.5 -@export var sprint_speed: float = 7.2 -@export var sneak_speed: float = 1.8 +@export var walk_speed: float = 2.25 +@export var sprint_speed: float = 4.5 +@export var sneak_speed: float = 1.125 @export var slow_walk_speed: float = 2.9 @export_range(0.1, 20.0, 0.1) var jump_velocity: float = 4.6 @export_range(0.1, 5.0, 0.05) var upward_gravity_multiplier: float = 1.35 @@ -69,7 +301,7 @@ class ShowcaseCameraSnapshot: @export var local_control_enabled: bool = true @export_category("Fishing Stats") -@export_range(0.01, 2.0, 0.01) var reel_speed: float = 0.32 +@export_range(0.01, 2.0, 0.01) var reel_speed: float = BASE_REEL_SPEED @export_range(1, 100, 1) var click_power: int = 1 @export_category("Showcase") @@ -93,8 +325,15 @@ class ShowcaseCameraSnapshot: @export var maximum_zoom: float = 8.0 @export var zoom_step: float = 0.75 @export var zoom_smoothing: float = 12.0 +@export_range(0.1, 12.0, 0.1) var controller_zoom_speed: float = 4.0 +@export_range(0.0, 1.0, 0.01) var controller_trigger_threshold: float = 0.55 +@export_range(1.0, 40.0, 0.5) var free_camera_speed: float = 10.0 +@export_range(1.0, 10.0, 0.5) var free_camera_sprint_multiplier: float = 3.0 @onready var _visuals: Node3D = %Visuals +@onready var _character_rig: Node3D = get_node_or_null( + "Visuals/CharacterRig/CharacterRig" +) as Node3D @onready var _character_animation_player: AnimationPlayer = ( get_node_or_null("Visuals/CharacterRig/AnimationPlayer") as AnimationPlayer ) @@ -102,6 +341,7 @@ class ShowcaseCameraSnapshot: @onready var _camera_pitch: Node3D = %CameraPitch @onready var _spring_arm: SpringArm3D = %SpringArm3D @onready var _camera: Camera3D = %Camera3D +@onready var _player_collision_shape: CollisionShape3D = $CollisionShape3D @onready var inventory: FishInventoryType = %Inventory @onready var collection_log: CollectionLogType = %CollectionLog @onready var wallet: PlayerWalletType = %Wallet @@ -114,16 +354,27 @@ class ShowcaseCameraSnapshot: @onready var art_unlocks: PlayerArtUnlocksType = %ArtUnlocks @onready var experience: PlayerExperienceType = %Experience @onready var _cast_origin: Marker3D = %CastOrigin -@onready var _catch_display: Node3D = %CatchDisplay -@onready var _catch_sprite: Sprite3D = %CatchSprite -@onready var _held_fish_display: Node3D = %HeldFishDisplay -@onready var _held_fish_sprite: Sprite3D = %HeldFishSprite -@onready var _held_art_kit_sprite: Sprite3D = %HeldArtKitSprite +var _catch_display: Node3D +var _catch_sprite: Sprite3D +var _held_item_attachment: BoneAttachment3D +var _held_fish_display: Node3D +var _held_fish_attachment_offset: Vector3 = Vector3(0.0, 0.08, 0.04) +var _held_fish_sprite: Sprite3D +var _held_art_kit_display: Node3D +var _held_art_kit_sprite: Sprite3D +var _catch_attachment_offset: Vector3 = Vector3(0.0, 0.08, 0.04) var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity")) var _camera_dragging: bool = false var _camera_input_enabled: bool = true +var _camera_drag_prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE +var _free_camera_active: bool = false +var _free_camera_body: CharacterBody3D +var _free_camera: Camera3D +var _free_camera_yaw: float = 0.0 +var _free_camera_pitch: float = 0.0 var _movement_enabled: bool = true +var _local_input_suppressors: Dictionary[StringName, bool] = {} var _water_recovery_active: bool = false var _remote_recovery_presentation_active: bool = false var _remote_recovery_visual_origin: Vector3 @@ -152,18 +403,162 @@ var _last_network_input_sequence: int = 0 var _network_target_position: Vector3 var _network_target_velocity: Vector3 var _network_target_visual_yaw: float = 0.0 +var _network_target_grounded: bool = false +var _network_target_locomotion_state: LocomotionState = LocomotionState.IDLE +var _network_target_animation_action_id: StringName = &"" +var _network_target_animation_action_sequence: int = 0 +var _network_target_animation_action_elapsed: float = 0.0 var _network_snapshot_ready: bool = false +var _network_snapshot_age: float = 0.0 +var _animation_action_id: StringName = &"" +var _animation_action_sequence: int = 0 +var _animation_action_elapsed: float = 0.0 +var _presented_animation_action_id: StringName = &"" +var _presented_animation_action_sequence: int = -1 var _character_animation_name: StringName = &"" var _sitting: bool = false +var _sit_after_landing: bool = false +var _sitting_intent_pending: bool = false +var _sitting_intent_sequence: int = -1 +var _held_fish_visible: bool = false +var _pending_held_fish_texture: Texture2D +var _pending_held_fish_scale: Vector3 = Vector3.ONE +var _held_art_kit_visible: bool = false +var _showcase_animation_active: bool = false +var _pocket_visual_active: bool = false +var _pocket_visual_target: PocketVisualTarget = PocketVisualTarget.NONE +var _pocket_visual_animation: StringName = &"" +var _pocket_visual_finished_callback: Callable +var _pocket_visual_callback_runs_on_interrupt: bool = true +var _pocket_visual_midpoint_callback: Callable +var _pocket_visual_midpoint_called: bool = false +var _pocket_visual_generation: int = 0 +var _fighting_visual_active: bool = false +var _blink_visual_active: bool = false +var _blink_seconds_remaining: float = 0.0 +var _blink_rng := RandomNumberGenerator.new() +var _character_call_mouth_active: bool = false +var _character_call_visual_generation: int = 0 +var _fishing_visual_active: bool = false +var _fishing_visual_phase: FishingVisualPhase = FishingVisualPhase.NONE +var _fishing_after_release_pending: bool = false +var _retract_animation_completed: bool = true var _fishing_rod: Node3D var _fishing_rod_tip: Marker3D +var _active_item_is_rod: bool = false +var _controller_mapping_manager: ControllerMappingManagerType +var _sprint_dust_landing_ready: bool = false +var _sprint_dust_airborne: bool = false +var _sprint_dust_fall_speed: float = 0.0 + +@onready var _sprint_dust: SprintDustTrailType = %SprintDust func _ready() -> void: + _blink_rng.randomize() + _schedule_next_blink() + if ( + _character_animation_player != null + and not _character_animation_player.animation_finished.is_connected( + _on_character_animation_finished + ) + ): + _character_animation_player.animation_finished.connect( + _on_character_animation_finished + ) + if not bag.contents_changed.is_connected(_on_bag_contents_changed): + bag.contents_changed.connect(_on_bag_contents_changed) + _apply_presented_appearance() _initialize_fishing_rod() _target_zoom = clampf(_spring_arm.spring_length, minimum_zoom, maximum_zoom) _spring_arm.spring_length = _target_zoom _camera.current = local_control_enabled + _initialize_held_item_attachment() + + +func _on_bag_contents_changed() -> void: + if ( + not active_bait_id.is_empty() + and bag.get_quantity(active_bait_id) <= 0 + ): + unequip_bait() + if ( + not active_lure_id.is_empty() + and not bag.owns_item(active_lure_id) + ): + unequip_lure() + + +func set_controller_mapping_manager( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + + +func begin_animation_action( + action_id: StringName, + elapsed_seconds: float = 0.0, +) -> bool: + if ( + action_id.is_empty() + or not supports_network_animation_action(action_id) + or _character_animation_player == null + or not _character_animation_player.has_animation(action_id) + ): + return false + var next_sequence: int = ( + 1 + if _animation_action_sequence + >= NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE + else _animation_action_sequence + 1 + ) + var action_state: Dictionary = ( + NetworkPlayerAnimationProtocol.make_action_state( + action_id, next_sequence, elapsed_seconds + ) + ) + if not NetworkPlayerAnimationProtocol.validate_action_state(action_state): + return false + _apply_animation_action_state(action_state) + return true + + +func end_animation_action() -> void: + if _animation_action_id.is_empty(): + return + var next_sequence: int = ( + 1 + if _animation_action_sequence + >= NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE + else _animation_action_sequence + 1 + ) + _apply_animation_action_state( + NetworkPlayerAnimationProtocol.make_action_state( + &"", next_sequence, 0.0 + ) + ) + + +func _apply_animation_action_state(action_state: Dictionary) -> void: + if not NetworkPlayerAnimationProtocol.validate_action_state(action_state): + return + var action_id := StringName(str(action_state["id"])) + if ( + not action_id.is_empty() + and not supports_network_animation_action(action_id) + ): + action_id = &"" + var action_sequence: int = int(action_state["sequence"]) + var action_changed: bool = ( + action_id != _animation_action_id + or action_sequence != _animation_action_sequence + ) + _animation_action_id = action_id + _animation_action_sequence = action_sequence + _animation_action_elapsed = float(action_state["elapsed"]) + if action_changed: + _character_animation_name = &"" + _update_character_animation() func _initialize_fishing_rod() -> void: @@ -184,20 +579,60 @@ func _initialize_fishing_rod() -> void: ) as Marker3D +func _initialize_held_item_attachment() -> void: + var skeleton := get_node_or_null( + "Visuals/CharacterRig/CharacterRig/Skeleton3D" + ) as Skeleton3D + if skeleton == null: + push_error("Player character skeleton is unavailable for held items.") + return + var attachment := HeldItemAttachmentScene.instantiate() as BoneAttachment3D + if attachment == null: + push_error("Held item attachment could not be instantiated.") + return + skeleton.add_child(attachment) + _held_item_attachment = attachment + _held_fish_display = attachment.get_node("HeldFishDisplay") as Node3D + _held_fish_attachment_offset = _held_fish_display.position + _held_fish_sprite = attachment.get_node( + "HeldFishDisplay/HeldFishSprite" + ) as Sprite3D + _held_art_kit_display = attachment.get_node("HeldArtKitDisplay") as Node3D + _held_art_kit_sprite = attachment.get_node( + "HeldArtKitDisplay/HeldArtKitSprite" + ) as Sprite3D + _catch_display = attachment.get_node("CatchDisplay") as Node3D + _catch_attachment_offset = _catch_display.position + _catch_sprite = attachment.get_node("CatchDisplay/CatchSprite") as Sprite3D + + func _physics_process(delta: float) -> void: if _network_interpolation_enabled: _update_network_interpolation(delta) return + if local_control_enabled and _free_camera_active: + _update_free_camera_physics() + velocity.x = 0.0 + velocity.z = 0.0 + if not is_on_floor(): + velocity.y -= _gravity * fall_gravity_multiplier * delta + move_and_slide() + _network_jump_pending = false + return var jump_requested: bool = ( - _movement_enabled + _is_movement_input_enabled() + and not _free_camera_active and ( (local_control_enabled and Input.is_action_just_pressed("jump")) or (_network_authoritative_simulation and _network_jump_pending) ) ) + if _sit_after_landing and is_on_floor(): + _sit_after_landing = false + _set_sitting(true, local_control_enabled) if _sitting: if jump_requested: - _set_sitting(false) + _set_sitting(false, local_control_enabled) else: velocity = Vector3.ZERO _network_jump_pending = false @@ -218,7 +653,7 @@ func _physics_process(delta: float) -> void: velocity.y = jump_velocity _network_jump_pending = false - if not _movement_enabled: + if not _is_movement_input_enabled(): velocity.x = 0.0 velocity.z = 0.0 move_and_slide() @@ -264,7 +699,30 @@ func _physics_process(delta: float) -> void: func _process(delta: float) -> void: + if not _animation_action_id.is_empty(): + _animation_action_elapsed = minf( + _animation_action_elapsed + delta, + NetworkPlayerAnimationProtocol.MAX_ACTION_ELAPSED_SECONDS, + ) + _update_blink(delta) _update_character_animation() + _update_sprint_dust(delta) + # The hand bone supplies the attachment position, but its animated wrist + # rotation should not turn the flat fish/catch artwork edge-on. Keep each + # display aligned with the character's facing while it follows the hand. + if _character_rig != null and _held_item_attachment != null: + var facing_rotation: Vector3 = _character_rig.global_rotation + for display: Node3D in [_held_fish_display, _catch_display]: + if display != null: + var attachment_offset: Vector3 = ( + _held_fish_attachment_offset + if display == _held_fish_display + else _catch_attachment_offset + ) + display.global_position = ( + _held_item_attachment.global_transform * attachment_offset + ) + display.global_rotation = facing_rotation if _remote_recovery_presentation_active: _remote_recovery_elapsed += delta _visuals.position = ( @@ -275,30 +733,35 @@ func _process(delta: float) -> void: ) if not local_control_enabled: return + if _free_camera_active: + return - if ( - _camera_input_enabled - and _camera_dragging - and not Input.is_action_pressed("camera_drag") - ): - _camera_dragging = false + if _camera_dragging and not Input.is_action_pressed("camera_drag"): + _set_camera_dragging(false) - if _camera_input_enabled: - var stick: Vector2 = Vector2( - Input.get_joy_axis(0, JOY_AXIS_RIGHT_X), - Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y) - ) + if _is_camera_input_enabled(): + var stick: Vector2 = _get_controller_camera_stick() if stick.length() > controller_camera_deadzone: - var adjusted_strength: float = ( - (stick.length() - controller_camera_deadzone) - / (1.0 - controller_camera_deadzone) - ) - _rotate_camera( - stick.normalized() - * adjusted_strength - * controller_camera_speed - * delta - ) + if ( + _get_controller_zoom_strength() + >= controller_trigger_threshold + ): + var vertical_zoom_input: float = _apply_axis_deadzone(stick.y) + _set_target_zoom( + _target_zoom + + vertical_zoom_input * controller_zoom_speed * delta + ) + else: + var adjusted_strength: float = ( + (stick.length() - controller_camera_deadzone) + / (1.0 - controller_camera_deadzone) + ) + _rotate_camera( + stick.normalized() + * adjusted_strength + * controller_camera_speed + * delta + ) var zoom_weight: float = 1.0 - exp(-zoom_smoothing * delta) _spring_arm.spring_length = lerpf( @@ -308,46 +771,380 @@ func _process(delta: float) -> void: ) +func _update_blink(delta: float) -> void: + if _fighting_visual_active: + return + _blink_seconds_remaining -= delta + if _blink_seconds_remaining > 0.0: + return + _blink_visual_active = not _blink_visual_active + if _blink_visual_active: + _blink_seconds_remaining = BLINK_DURATION_SECONDS + else: + _schedule_next_blink() + _apply_presented_appearance() + + +func _schedule_next_blink() -> void: + _blink_seconds_remaining = _blink_rng.randf_range( + BLINK_INTERVAL_SECONDS.x, + BLINK_INTERVAL_SECONDS.y, + ) + + +func _update_sprint_dust(delta: float) -> void: + if _sprint_dust == null: + return + var horizontal_velocity := Vector3(velocity.x, 0.0, velocity.z) + var grounded: bool = _get_presented_grounded() + if _water_recovery_active: + _sprint_dust_airborne = false + _sprint_dust_fall_speed = 0.0 + elif not _sprint_dust_landing_ready: + # Establish a real ground contact before arming landing effects. This + # prevents the player's initial placement from looking like a fall. + _sprint_dust_landing_ready = grounded + elif not grounded: + _sprint_dust_airborne = true + _sprint_dust_fall_speed = maxf( + _sprint_dust_fall_speed, + maxf(-velocity.y, 0.0), + ) + elif _sprint_dust_airborne: + if _sprint_dust_fall_speed >= LANDING_DUST_MIN_FALL_SPEED: + var landing_facing: Vector3 = horizontal_velocity.normalized() + if landing_facing.is_zero_approx() and _visuals != null: + landing_facing = -_visuals.global_basis.z + _sprint_dust.emit_landing_burst( + global_position, + landing_facing, + ) + _sprint_dust_airborne = false + _sprint_dust_fall_speed = 0.0 + var should_emit: bool = ( + grounded + and not _sitting + and not _water_recovery_active + and _get_presented_locomotion_state() == LocomotionState.RUNNING + ) + _sprint_dust.update_trail( + delta, + global_position, + horizontal_velocity, + should_emit, + ) + + +func _get_controller_camera_stick() -> Vector2: + if _controller_mapping_manager != null: + return Vector2( + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X + ), + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y + ), + ) + return Vector2( + Input.get_joy_axis(0, JOY_AXIS_RIGHT_X), + Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y), + ) + + +func _get_controller_zoom_strength() -> float: + if _controller_mapping_manager != null: + return _controller_mapping_manager.get_role_strength( + ControllerMappingManagerType.ROLE_CAMERA_ZOOM + ) + return Input.get_joy_axis(0, CONTROLLER_ZOOM_TRIGGER_AXIS) + + func _update_character_animation() -> void: if _character_animation_player == null: return - var horizontal_speed_squared: float = ( - velocity.x * velocity.x + velocity.z * velocity.z + var locomotion_state: LocomotionState = ( + _get_presented_locomotion_state() ) - var next_animation: StringName = ( - CHARACTER_SITTING_ANIMATION - if _sitting - else ( - CHARACTER_WALKING_ANIMATION - if horizontal_speed_squared > 0.0025 - else CHARACTER_IDLE_ANIMATION + var is_walking: bool = locomotion_state != LocomotionState.IDLE + var is_running: bool = locomotion_state == LocomotionState.RUNNING + var animation_action: Dictionary = _get_presented_animation_action() + var animation_action_id := StringName(str(animation_action.get("id", ""))) + var animation_action_available: bool = ( + not animation_action_id.is_empty() + and supports_network_animation_action(animation_action_id) + and _character_animation_player.has_animation(animation_action_id) + ) + var held_show_item_visible: bool = ( + _held_fish_visible or _held_art_kit_visible + ) + var requested_animation: Array[StringName] = [] + if _pocket_visual_active: + requested_animation = [_pocket_visual_animation] + elif _showcase_animation_active: + if _sitting: + requested_animation = [ + CHARACTER_IDLE_SIT_SHOW_ANIMATION, + &"idle_sit_show_loop", + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + &"idle_loop_sit", + ] + else: + requested_animation = [ + CHARACTER_IDLE_SHOW_ANIMATION, + &"idle_show_loop", + &"show", + &"show_loop", + ] + elif _fighting_visual_active: + if _sitting: + requested_animation = [ + CHARACTER_FIGHTING_SIT_ANIMATION, + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + ] + else: + requested_animation = [ + CHARACTER_FIGHTING_ANIMATION, + CHARACTER_IDLE_ANIMATION, + ] + elif _fishing_visual_active: + if _sitting: + match _fishing_visual_phase: + FishingVisualPhase.CASTING: + requested_animation = [ + CHARACTER_CASTING_SIT_ANIMATION, + CHARACTER_FISHING_SIT_ANIMATION, + CHARACTER_IDLE_SIT_ANIMATION, + ] + FishingVisualPhase.RELEASE: + requested_animation = [ + CHARACTER_RELEASE_SIT_ANIMATION, + CHARACTER_FISHING_SIT_ANIMATION, + CHARACTER_IDLE_SIT_ANIMATION, + ] + FishingVisualPhase.RETRACT: + requested_animation = [ + CHARACTER_RETRACT_SIT_ANIMATION, + CHARACTER_RETRACT_ANIMATION, + CHARACTER_FISHING_SIT_ANIMATION, + CHARACTER_IDLE_SIT_ANIMATION, + ] + _: + requested_animation = [ + CHARACTER_FISHING_SIT_ANIMATION, + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + ] + else: + match _fishing_visual_phase: + FishingVisualPhase.CASTING: + requested_animation = [ + CHARACTER_CASTING_ANIMATION, + CHARACTER_FISHING_ANIMATION, + CHARACTER_IDLE_ANIMATION, + ] + FishingVisualPhase.RELEASE: + requested_animation = [ + CHARACTER_RELEASE_ANIMATION, + CHARACTER_FISHING_ANIMATION, + CHARACTER_IDLE_ANIMATION, + ] + FishingVisualPhase.RETRACT: + requested_animation = [ + CHARACTER_RETRACT_ANIMATION, + CHARACTER_FISHING_ANIMATION, + CHARACTER_IDLE_ANIMATION, + ] + _: + requested_animation = [ + CHARACTER_FISHING_ANIMATION, + CHARACTER_IDLE_ANIMATION, + ] + elif animation_action_available: + requested_animation = [animation_action_id] + elif _sitting: + if held_show_item_visible: + requested_animation = [ + CHARACTER_IDLE_SIT_SHOW_ANIMATION, + &"idle_sit_show_loop", + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + &"idle_loop_sit", + ] + else: + requested_animation = [ + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + &"sitting", + &"idle_loop_sit", + ] + elif is_running and held_show_item_visible: + requested_animation = [ + CHARACTER_RUNNING_SHOW_ANIMATION, + &"running_show_loop", + CHARACTER_WALKING_SHOW_ANIMATION, + &"walking_show_loop", + ] + elif is_running: + requested_animation = [ + CHARACTER_RUNNING_ANIMATION, + &"running_loop", + CHARACTER_WALKING_ANIMATION, + &"walking_loop", + ] + elif is_walking and held_show_item_visible: + requested_animation = [ + CHARACTER_WALKING_SHOW_ANIMATION, + &"walking_show_loop", + ] + elif held_show_item_visible: + requested_animation = [ + CHARACTER_IDLE_SHOW_ANIMATION, + &"idle_show_loop", + &"show", + &"show_loop", + ] + elif is_walking: + requested_animation = [ + CHARACTER_WALKING_ANIMATION, + &"walking_loop", + ] + else: + requested_animation = [CHARACTER_IDLE_ANIMATION, &"idle_loop"] + var next_animation: StringName = &"" + for candidate: StringName in requested_animation: + if _character_animation_player.has_animation(candidate): + next_animation = candidate + break + if next_animation.is_empty(): + return + var action_selected: bool = ( + animation_action_available and next_animation == animation_action_id + ) + var action_sequence: int = int(animation_action.get("sequence", 0)) + var action_changed: bool = ( + action_selected + and ( + animation_action_id != _presented_animation_action_id + or action_sequence != _presented_animation_action_sequence ) ) - if not _character_animation_player.has_animation(next_animation): - return - if _character_animation_name == next_animation: + if not action_selected: + _presented_animation_action_id = &"" + _presented_animation_action_sequence = -1 + if _character_animation_name == next_animation and not action_changed: return _character_animation_player.play(next_animation) _character_animation_name = next_animation + if action_selected: + _presented_animation_action_id = animation_action_id + _presented_animation_action_sequence = action_sequence + var elapsed: float = float(animation_action.get("elapsed", 0.0)) + var animation: Animation = _character_animation_player.get_animation( + next_animation + ) + if elapsed > 0.0 and animation != null and animation.length > 0.0: + _character_animation_player.seek( + fposmod(elapsed, animation.length), true + ) + + +func _on_character_animation_finished(animation_name: StringName) -> void: + if ( + local_control_enabled + and not _animation_action_id.is_empty() + and animation_name == _animation_action_id + ): + end_animation_action() + return + if animation_name in [ + CHARACTER_POCKET_IDLE_IDLE_ANIMATION, + CHARACTER_POCKET_IDLE_SHOW_ANIMATION, + CHARACTER_POCKET_SHOW_SHOW_ANIMATION, + CHARACTER_POCKET_SHOW_IDLE_ANIMATION, + CHARACTER_POCKET_SIT_IDLE_IDLE_ANIMATION, + CHARACTER_POCKET_SIT_IDLE_SHOW_ANIMATION, + CHARACTER_POCKET_SIT_SHOW_SHOW_ANIMATION, + CHARACTER_POCKET_SIT_SHOW_IDLE_ANIMATION, + CHARACTER_POCKET_WALKING_IDLE_IDLE_ANIMATION, + CHARACTER_POCKET_WALKING_IDLE_SHOW_ANIMATION, + CHARACTER_POCKET_WALKING_SHOW_SHOW_ANIMATION, + CHARACTER_POCKET_WALKING_SHOW_IDLE_ANIMATION, + ]: + if _pocket_visual_active: + _complete_pocket_visual() + return + if animation_name in [ + CHARACTER_RETRACT_ANIMATION, + CHARACTER_RETRACT_SIT_ANIMATION, + ]: + _complete_retract_animation() + return + if ( + not _fishing_after_release_pending + or animation_name not in [ + CHARACTER_RELEASE_ANIMATION, + CHARACTER_RELEASE_SIT_ANIMATION, + ] + ): + return + _fishing_after_release_pending = false + _set_fishing_visual_phase(FishingVisualPhase.FISHING) + + +func is_retract_visual_complete() -> bool: + return _retract_animation_completed + + +func _complete_retract_animation() -> void: + if _retract_animation_completed: + return + _retract_animation_completed = true + retract_visual_finished.emit() func toggle_sitting() -> void: + if _sit_after_landing: + _sit_after_landing = false + return var should_sit: bool = not _sitting if should_sit: if ( _character_animation_player == null - or not _character_animation_player.has_animation( - CHARACTER_SITTING_ANIMATION - ) + or not _has_any_character_animation([ + CHARACTER_IDLE_SIT_ANIMATION, + &"idle_sit_loop", + &"sitting", + &"idle_loop_sit", + ]) ): return - _set_sitting(should_sit) + if not is_on_floor(): + _sit_after_landing = true + return + _set_sitting(should_sit, local_control_enabled) -func _set_sitting(should_sit: bool) -> void: +func _has_any_character_animation(candidates: Array[StringName]) -> bool: + if _character_animation_player == null: + return false + for candidate: StringName in candidates: + if _character_animation_player.has_animation(candidate): + return true + return false + + +func _set_sitting( + should_sit: bool, + is_local_intent: bool = false, +) -> void: if _sitting == should_sit: return _sitting = should_sit + if is_local_intent: + _sitting_intent_pending = true + _sitting_intent_sequence = -1 if _sitting: velocity = Vector3.ZERO _character_animation_name = &"" @@ -359,31 +1156,50 @@ func is_sitting() -> bool: func _unhandled_input(event: InputEvent) -> void: - if not local_control_enabled or not _camera_input_enabled: + if not local_control_enabled or not _is_camera_input_enabled(): + return + if event.is_action_pressed("toggle_free_camera"): + toggle_free_camera() + get_viewport().set_input_as_handled() return if event.is_action("camera_drag"): - _camera_dragging = event.is_pressed() + _set_camera_dragging(event.is_pressed()) get_viewport().set_input_as_handled() return if event is InputEventMouseMotion and _camera_dragging: - _rotate_camera(event.relative * mouse_sensitivity) + if _free_camera_active: + _rotate_free_camera(event.relative * mouse_sensitivity) + else: + _rotate_camera(event.relative * mouse_sensitivity) get_viewport().set_input_as_handled() return + if _free_camera_active: + return - if ( + var mouse_zoom_in: bool = ( event is InputEventMouseButton and event.shift_pressed and event.is_action_pressed("camera_zoom_in") - ): - _set_target_zoom(_target_zoom - zoom_step) - get_viewport().set_input_as_handled() - elif ( + ) + var controller_zoom_in: bool = ( + event is InputEventJoypadButton + and event.is_action_pressed("camera_zoom_in") + ) + var mouse_zoom_out: bool = ( event is InputEventMouseButton and event.shift_pressed and event.is_action_pressed("camera_zoom_out") - ): + ) + var controller_zoom_out: bool = ( + event is InputEventJoypadButton + and event.is_action_pressed("camera_zoom_out") + ) + if mouse_zoom_in or controller_zoom_in: + _set_target_zoom(_target_zoom - zoom_step) + get_viewport().set_input_as_handled() + elif mouse_zoom_out or controller_zoom_out: _set_target_zoom(_target_zoom + zoom_step) get_viewport().set_input_as_handled() @@ -398,6 +1214,17 @@ func _get_current_speed() -> float: return walk_speed +func _set_camera_dragging(active: bool) -> void: + if _camera_dragging == active: + return + _camera_dragging = active + if active: + _camera_drag_prior_mouse_mode = Input.mouse_mode + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + elif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: + Input.mouse_mode = _camera_drag_prior_mouse_mode + + func _get_network_aware_speed() -> float: if local_control_enabled: return _get_current_speed() @@ -420,16 +1247,138 @@ func _rotate_camera(delta_rotation: Vector2) -> void: ) +func _set_free_camera_active(active: bool) -> void: + if _free_camera_active == active: + return + if active: + var camera_transform: Transform3D = _camera.global_transform + var duplicated_camera := _camera.duplicate() as Camera3D + if duplicated_camera == null: + return + var camera_body := CharacterBody3D.new() + camera_body.name = "FreeCameraBody" + camera_body.collision_layer = 0 + camera_body.collision_mask = collision_mask + camera_body.motion_mode = CharacterBody3D.MOTION_MODE_FLOATING + add_child(camera_body) + camera_body.top_level = true + camera_body.global_position = camera_transform.origin + var camera_collision := CollisionShape3D.new() + camera_collision.name = "FreeCameraCollision" + camera_collision.shape = _player_collision_shape.shape + camera_body.add_child(camera_collision) + duplicated_camera.name = "FreeCamera" + duplicated_camera.unique_name_in_owner = false + camera_body.add_child(duplicated_camera) + duplicated_camera.transform = Transform3D( + camera_transform.basis, + Vector3.ZERO, + ) + _free_camera_body = camera_body + _free_camera = duplicated_camera + var camera_rotation: Vector3 = _free_camera.global_rotation + _free_camera_yaw = camera_rotation.y + _free_camera_pitch = camera_rotation.x + _camera.current = false + _free_camera.current = true + _free_camera_active = true + return + _free_camera_active = false + _set_camera_dragging(false) + if _free_camera != null: + _free_camera.current = false + _free_camera = null + if _free_camera_body != null: + _free_camera_body.queue_free() + _free_camera_body = null + _camera.current = local_control_enabled + + +func toggle_free_camera() -> void: + if not local_control_enabled: + return + _set_free_camera_active(not _free_camera_active) + + +func is_free_camera_active() -> bool: + return _free_camera_active + + +func _update_free_camera_physics() -> void: + if _free_camera == null or _free_camera_body == null: + _set_free_camera_active(false) + return + var input_vector: Vector2 = Input.get_vector( + "move_left", + "move_right", + "move_forward", + "move_backward", + ) + var vertical_input: float = ( + Input.get_action_strength("jump") + - Input.get_action_strength("sneak") + ) + var yaw_basis := Basis(Vector3.UP, _free_camera_yaw) + var movement: Vector3 = ( + yaw_basis.x * input_vector.x + + yaw_basis.z * input_vector.y + + Vector3.UP * vertical_input + ) + if movement.length_squared() > 1.0: + movement = movement.normalized() + var movement_speed: float = free_camera_speed + if Input.is_action_pressed("sprint"): + movement_speed *= free_camera_sprint_multiplier + _free_camera_body.velocity = movement * movement_speed + _free_camera_body.move_and_slide() + + +func _rotate_free_camera(delta_rotation: Vector2) -> void: + if _free_camera == null: + return + _free_camera_yaw -= delta_rotation.x + var vertical_direction: float = -1.0 if invert_camera_y else 1.0 + _free_camera_pitch = clampf( + _free_camera_pitch - delta_rotation.y * vertical_direction, + deg_to_rad(-90.0), + deg_to_rad(90.0), + ) + _free_camera.global_rotation = Vector3( + _free_camera_pitch, + _free_camera_yaw, + 0.0, + ) + + +func get_active_gameplay_camera() -> Camera3D: + if _free_camera_active and _free_camera != null: + return _free_camera + return _camera + + func _set_target_zoom(value: float) -> void: _target_zoom = clampf(value, minimum_zoom, maximum_zoom) +func _apply_axis_deadzone(value: float) -> float: + var magnitude: float = absf(value) + if magnitude <= controller_camera_deadzone: + return 0.0 + return ( + signf(value) + * (magnitude - controller_camera_deadzone) + / (1.0 - controller_camera_deadzone) + ) + + func set_local_control(enabled: bool) -> void: + if not enabled and _free_camera_active: + _set_free_camera_active(false) local_control_enabled = enabled if is_node_ready(): _camera.current = enabled if not enabled: - _camera_dragging = false + _set_camera_dragging(false) func set_network_peer_id(peer_id: int) -> void: @@ -445,11 +1394,23 @@ func configure_network_remote(authoritative_simulation: bool) -> void: _network_authoritative_simulation = authoritative_simulation _network_interpolation_enabled = not authoritative_simulation _network_snapshot_ready = false + _network_snapshot_age = 0.0 + _network_target_grounded = false + _network_target_locomotion_state = LocomotionState.IDLE + _network_target_animation_action_id = &"" + _network_target_animation_action_sequence = 0 + _network_target_animation_action_elapsed = 0.0 _camera.current = false func capture_network_input(sequence: int) -> Dictionary: - if not _movement_enabled or _water_recovery_active: + if _sitting_intent_pending and _sitting_intent_sequence < 0: + _sitting_intent_sequence = sequence + if ( + not _is_movement_input_enabled() + or _water_recovery_active + or _free_camera_active + ): return { "sequence": sequence, "axis": [0.0, 0.0], @@ -458,6 +1419,11 @@ func capture_network_input(sequence: int) -> Dictionary: "sprint": false, "sneak": false, "slow_walk": false, + "sitting": _sitting, + "casting": ( + _fishing_visual_phase == FishingVisualPhase.CASTING + ), + "animation_action": _make_animation_action_state(), } var axis: Vector2 = Input.get_vector( "move_left", @@ -473,6 +1439,9 @@ func capture_network_input(sequence: int) -> Dictionary: "sprint": Input.is_action_pressed("sprint"), "sneak": Input.is_action_pressed("sneak"), "slow_walk": Input.is_action_pressed("slow_walk"), + "sitting": _sitting, + "casting": _fishing_visual_phase == FishingVisualPhase.CASTING, + "animation_action": _make_animation_action_state(), } @@ -490,6 +1459,15 @@ func apply_authoritative_network_input(data: Dictionary) -> void: _network_sprint = bool(data.get("sprint", false)) _network_sneak = bool(data.get("sneak", false)) _network_slow_walk = bool(data.get("slow_walk", false)) + _apply_animation_action_state(data.get("animation_action", {})) + _apply_network_casting(bool(data.get("casting", false))) + var sitting_requested: bool = bool(data.get("sitting", false)) + if sitting_requested and not is_on_floor(): + _sit_after_landing = true + _set_sitting(false) + else: + _sit_after_landing = false + _set_sitting(sitting_requested) func make_network_snapshot(peer_id: int) -> Dictionary: @@ -499,7 +1477,15 @@ func make_network_snapshot(peer_id: int) -> Dictionary: "position": [global_position.x, global_position.y, global_position.z], "velocity": [velocity.x, velocity.y, velocity.z], "visual_yaw": _visuals.rotation.y, - "grounded": is_on_floor(), + "animation_state": NetworkPlayerAnimationProtocol.make_state( + _get_authoritative_locomotion_id(), + is_on_floor(), + _animation_action_id, + _animation_action_sequence, + _animation_action_elapsed, + ), + "sitting": _sitting, + "casting": _fishing_visual_phase == FishingVisualPhase.CASTING, } @@ -510,6 +1496,10 @@ func push_network_snapshot(snapshot: Dictionary) -> void: _network_target_position = parsed["position"] _network_target_velocity = parsed["velocity"] _network_target_visual_yaw = parsed["visual_yaw"] + _apply_network_target_animation_state(parsed["animation_state"]) + _network_snapshot_age = 0.0 + _apply_network_casting(bool(parsed["casting"])) + _set_sitting(bool(parsed["sitting"])) if not _network_snapshot_ready: global_position = _network_target_position velocity = _network_target_velocity @@ -521,6 +1511,17 @@ func apply_local_prediction_correction(snapshot: Dictionary) -> void: var parsed: Dictionary = _parse_network_snapshot(snapshot) if parsed.is_empty(): return + var acknowledged_input: int = parsed["acknowledged_input"] + var sitting_intent_acknowledged: bool = ( + _sitting_intent_pending + and _sitting_intent_sequence >= 0 + and acknowledged_input >= _sitting_intent_sequence + ) + if sitting_intent_acknowledged: + _sitting_intent_pending = false + _sitting_intent_sequence = -1 + if not _sitting_intent_pending: + _set_sitting(bool(parsed["sitting"])) var authoritative_position: Vector3 = parsed["position"] var error_distance: float = global_position.distance_to( authoritative_position @@ -538,9 +1539,13 @@ func apply_network_teleport(snapshot: Dictionary) -> void: global_position = parsed["position"] velocity = parsed["velocity"] _visuals.rotation.y = parsed["visual_yaw"] + _apply_network_casting(bool(parsed["casting"])) + _set_sitting(bool(parsed["sitting"])) _network_target_position = global_position _network_target_velocity = velocity _network_target_visual_yaw = _visuals.rotation.y + _apply_network_target_animation_state(parsed["animation_state"]) + _network_snapshot_age = 0.0 _network_snapshot_ready = true @@ -565,6 +1570,29 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary: float(network_velocity[2]) ) var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0)) + var animation_state_value: Variant = snapshot.get("animation_state") + if not NetworkPlayerAnimationProtocol.validate_state(animation_state_value): + return {} + var animation_state: Dictionary = ( + animation_state_value as Dictionary + ).duplicate(true) + if ( + snapshot.has("acknowledged_input") + and ( + typeof(snapshot.get("acknowledged_input")) != TYPE_INT + or int(snapshot.get("acknowledged_input")) < 0 + ) + ): + return {} + var acknowledged_input: int = int( + snapshot.get("acknowledged_input", 0) + ) + if snapshot.has("sitting") and typeof(snapshot.get("sitting")) != TYPE_BOOL: + return {} + var sitting: bool = bool(snapshot.get("sitting", false)) + if snapshot.has("casting") and typeof(snapshot.get("casting")) != TYPE_BOOL: + return {} + var casting: bool = bool(snapshot.get("casting", false)) if ( not parsed_position.is_finite() or not parsed_velocity.is_finite() @@ -575,17 +1603,145 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary: "position": parsed_position, "velocity": parsed_velocity, "visual_yaw": visual_yaw, + "animation_state": animation_state, + "acknowledged_input": acknowledged_input, + "sitting": sitting, + "casting": casting, } +func _make_animation_action_state() -> Dictionary: + return NetworkPlayerAnimationProtocol.make_action_state( + _animation_action_id, + _animation_action_sequence, + _animation_action_elapsed, + ) + + +func _apply_network_target_animation_state(state: Dictionary) -> void: + if not NetworkPlayerAnimationProtocol.validate_state(state): + return + _network_target_grounded = bool(state["grounded"]) + _network_target_locomotion_state = _locomotion_state_from_id( + StringName(str(state["locomotion_id"])) + ) + var action: Dictionary = state["action"] + _network_target_animation_action_id = StringName(str(action["id"])) + _network_target_animation_action_sequence = int(action["sequence"]) + _network_target_animation_action_elapsed = float(action["elapsed"]) + + +func _get_authoritative_locomotion_state() -> LocomotionState: + if ( + _network_authoritative_simulation + and _is_movement_input_enabled() + and not _water_recovery_active + and not _sitting + and _network_axis.length_squared() > 0.0025 + ): + return ( + LocomotionState.RUNNING + if _network_sprint + else LocomotionState.WALKING + ) + return _locomotion_state_from_velocity() + + +func _get_authoritative_locomotion_id() -> StringName: + return _locomotion_id_from_state(_get_authoritative_locomotion_state()) + + +func _get_presented_locomotion_state() -> LocomotionState: + if _network_interpolation_enabled and _network_snapshot_ready: + return _network_target_locomotion_state + return _locomotion_state_from_velocity() + + +func _get_presented_animation_action() -> Dictionary: + if _network_interpolation_enabled and _network_snapshot_ready: + return NetworkPlayerAnimationProtocol.make_action_state( + _network_target_animation_action_id, + _network_target_animation_action_sequence, + minf( + _network_target_animation_action_elapsed + _network_snapshot_age, + NetworkPlayerAnimationProtocol.MAX_ACTION_ELAPSED_SECONDS, + ), + ) + return _make_animation_action_state() + + +func _locomotion_id_from_state(state: LocomotionState) -> StringName: + match state: + LocomotionState.WALKING: + return NetworkPlayerAnimationProtocol.LOCOMOTION_WALKING + LocomotionState.RUNNING: + return NetworkPlayerAnimationProtocol.LOCOMOTION_RUNNING + _: + return NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE + + +func _locomotion_state_from_id(state_id: StringName) -> LocomotionState: + match state_id: + NetworkPlayerAnimationProtocol.LOCOMOTION_WALKING: + return LocomotionState.WALKING + NetworkPlayerAnimationProtocol.LOCOMOTION_RUNNING: + return LocomotionState.RUNNING + _: + return LocomotionState.IDLE + + +static func supports_network_animation_action(action_id: StringName) -> bool: + return action_id in NETWORK_ANIMATION_ACTION_IDS + + +func _locomotion_state_from_velocity() -> LocomotionState: + var horizontal_speed_squared: float = ( + velocity.x * velocity.x + velocity.z * velocity.z + ) + if horizontal_speed_squared <= 0.0025: + return LocomotionState.IDLE + var fastest_non_sprint_speed := maxf( + walk_speed, + maxf(sneak_speed, slow_walk_speed) + ) + var running_threshold := fastest_non_sprint_speed + 0.5 + if horizontal_speed_squared > running_threshold * running_threshold: + return LocomotionState.RUNNING + return LocomotionState.WALKING + + +func _get_presented_grounded() -> bool: + if _network_interpolation_enabled and _network_snapshot_ready: + return _network_target_grounded + return is_on_floor() + + +func _apply_network_casting(casting: bool) -> void: + if casting: + if _fishing_visual_phase == FishingVisualPhase.NONE: + set_casting_visual() + elif _fishing_visual_phase == FishingVisualPhase.CASTING: + set_fishing_visual(false) + + func _update_network_interpolation(delta: float) -> void: if not _network_snapshot_ready: return - var position_weight: float = 1.0 - exp(-12.0 * delta) - global_position = global_position.lerp( - _network_target_position, - position_weight + _network_snapshot_age = minf(_network_snapshot_age + delta, 0.1) + var predicted_position: Vector3 = ( + _network_target_position + + _network_target_velocity * _network_snapshot_age ) + var projected_position: Vector3 = ( + global_position + _network_target_velocity * delta + ) + if projected_position.distance_to(predicted_position) > 2.0: + global_position = predicted_position + else: + global_position = projected_position.lerp( + predicted_position, + 1.0 - exp(-10.0 * delta) + ) velocity = _network_target_velocity _visuals.rotation.y = lerp_angle( _visuals.rotation.y, @@ -609,14 +1765,37 @@ func is_movement_enabled() -> bool: return _movement_enabled +func set_local_input_suppressed(owner: StringName, suppressed: bool) -> void: + if owner.is_empty(): + return + if suppressed: + _local_input_suppressors[owner] = true + velocity.x = 0.0 + velocity.z = 0.0 + _set_camera_dragging(false) + else: + _local_input_suppressors.erase(owner) + + +func _is_movement_input_enabled() -> bool: + return _movement_enabled and _local_input_suppressors.is_empty() + + +func _is_camera_input_enabled() -> bool: + return _camera_input_enabled and _local_input_suppressors.is_empty() + + func set_camera_input_enabled(enabled: bool) -> void: _camera_input_enabled = enabled if not enabled: - _camera_dragging = false + _set_camera_dragging(false) func set_camera_active(active: bool) -> void: - _camera.current = active + if _free_camera_active and _free_camera != null: + _free_camera.current = active + else: + _camera.current = active func apply_camera_settings( @@ -723,37 +1902,316 @@ func get_fishing_rod() -> Node3D: return _fishing_rod -func set_active_item_is_rod(active_is_rod: bool) -> void: +func set_active_item_is_rod( + active_is_rod: bool, + animate_transition: bool = false, +) -> void: + _active_item_is_rod = active_is_rod if _showcase_rod_state_stored: _showcase_rod_visibility = active_is_rod - else: + return + var visibility_changed: bool = _fishing_rod.visible != active_is_rod + if not animate_transition or not visibility_changed: _fishing_rod.visible = active_is_rod + return + if _has_held_show_item() or _showcase_animation_active: + if not active_is_rod: + _fishing_rod.visible = false + return + if active_is_rod: + _fishing_rod.visible = false + _begin_pocket_visual( + PocketVisualTarget.IDLE_ITEM, + false, + false, + _apply_active_rod_visibility, + ) + + +func _apply_active_rod_visibility() -> void: + if _showcase_rod_state_stored: + _showcase_rod_visibility = _active_item_is_rod + return + _fishing_rod.visible = _active_item_is_rod func set_active_art_kit(icon: Texture2D, should_show: bool) -> void: - _held_art_kit_sprite.texture = icon if should_show else null - _held_art_kit_sprite.visible = should_show and icon != null + var new_visible: bool = should_show and icon != null + if not new_visible: + if _held_art_kit_visible: + _begin_pocket_visual( + PocketVisualTarget.ART_KIT, + true, + _held_fish_visible, + _clear_active_art_kit, + true, + _apply_pending_held_fish, + ) + else: + _clear_active_art_kit() + return + var previous_show_pose: bool = _has_held_show_item() + var item_changed: bool = ( + not _held_art_kit_visible + or _held_art_kit_sprite.texture != icon + ) + _held_art_kit_visible = true + _held_art_kit_sprite.texture = icon + _held_art_kit_sprite.visible = true + if not previous_show_pose: + _held_art_kit_display.visible = false + _fishing_rod.visible = false + if item_changed: + _begin_pocket_visual( + PocketVisualTarget.ART_KIT, + previous_show_pose, + true, + Callable(), + false, + _apply_active_art_kit_visual, + ) + else: + _apply_active_art_kit_visual() + _update_character_animation() func set_held_fish( fish: FishDataType, display_scale: float, should_show: bool, + animate_put_away: bool = false, ) -> void: if not should_show or fish == null or fish.display_texture == null: - _held_fish_display.visible = false - _held_fish_display.scale = Vector3.ONE - _held_fish_sprite.texture = null + if animate_put_away and _held_fish_visible: + _begin_pocket_visual( + PocketVisualTarget.HELD_FISH, + true, + _held_art_kit_visible, + _clear_held_fish, + ) + else: + if _pocket_visual_target == PocketVisualTarget.HELD_FISH: + _cancel_pocket_visual() + _clear_held_fish() return - _held_fish_sprite.texture = fish.display_texture - _held_fish_display.scale = ( + var previous_show_pose: bool = _has_held_show_item() + var item_changed: bool = ( + not _held_fish_visible + or _held_fish_sprite.texture != fish.display_texture + ) + _held_fish_visible = true + _pending_held_fish_texture = fish.display_texture + _pending_held_fish_scale = ( Vector3.ONE * maxf(display_scale, 0.01) * catch_presentation_base_scale ) + if not previous_show_pose: + _held_fish_display.visible = false + _fishing_rod.visible = false + if item_changed: + _begin_pocket_visual( + PocketVisualTarget.HELD_FISH, + previous_show_pose, + true, + Callable(), + false, + _apply_pending_held_fish, + ) + else: + _apply_pending_held_fish() + _update_character_animation() + + +func _has_held_show_item() -> bool: + return _held_fish_visible or _held_art_kit_visible + + +func _clear_held_fish() -> void: + var was_visible: bool = _held_fish_visible + _held_fish_visible = false + _pending_held_fish_texture = null + _pending_held_fish_scale = Vector3.ONE + _held_fish_display.visible = false + _held_fish_display.scale = Vector3.ONE + _held_fish_sprite.texture = null + if was_visible: + _apply_active_rod_visibility() + _update_character_animation() + + +func _apply_pending_held_fish() -> void: + if not _held_fish_visible or _pending_held_fish_texture == null: + return + _held_fish_sprite.texture = _pending_held_fish_texture + _held_fish_display.scale = _pending_held_fish_scale _held_fish_display.visible = true +func _clear_active_art_kit() -> void: + var was_visible: bool = _held_art_kit_visible + _held_art_kit_visible = false + _held_art_kit_display.visible = false + _held_art_kit_sprite.texture = null + _held_art_kit_sprite.visible = false + if was_visible: + _apply_active_rod_visibility() + if _held_fish_visible: + _apply_pending_held_fish() + _update_character_animation() + + +func _apply_active_art_kit_visual() -> void: + if not _held_art_kit_visible or _held_art_kit_sprite.texture == null: + return + _held_art_kit_display.visible = true + + +func _begin_pocket_visual( + target: PocketVisualTarget, + starts_in_show_pose: bool, + ends_in_show_pose: bool, + finished_callback: Callable, + callback_runs_on_interrupt: bool = true, + midpoint_callback: Callable = Callable(), +) -> void: + var animation_name: StringName + if _sitting: + if starts_in_show_pose: + animation_name = ( + CHARACTER_POCKET_SIT_SHOW_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_SIT_SHOW_IDLE_ANIMATION + ) + else: + animation_name = ( + CHARACTER_POCKET_SIT_IDLE_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_SIT_IDLE_IDLE_ANIMATION + ) + elif _is_moving_for_pocket_visual(): + if starts_in_show_pose: + animation_name = ( + CHARACTER_POCKET_WALKING_SHOW_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_WALKING_SHOW_IDLE_ANIMATION + ) + else: + animation_name = ( + CHARACTER_POCKET_WALKING_IDLE_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_WALKING_IDLE_IDLE_ANIMATION + ) + elif starts_in_show_pose: + animation_name = ( + CHARACTER_POCKET_SHOW_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_SHOW_IDLE_ANIMATION + ) + else: + animation_name = ( + CHARACTER_POCKET_IDLE_SHOW_ANIMATION + if ends_in_show_pose + else CHARACTER_POCKET_IDLE_IDLE_ANIMATION + ) + if _pocket_visual_active: + if ( + _pocket_visual_target == target + and _pocket_visual_animation == animation_name + ): + return + if _pocket_visual_callback_runs_on_interrupt: + _complete_pocket_visual() + else: + _cancel_pocket_visual() + if ( + animation_name.is_empty() + or _character_animation_player == null + or not _character_animation_player.has_animation(animation_name) + ): + if midpoint_callback.is_valid(): + midpoint_callback.call() + if finished_callback.is_valid(): + finished_callback.call() + _update_character_animation() + return + _pocket_visual_active = true + _pocket_visual_target = target + _pocket_visual_animation = animation_name + _pocket_visual_finished_callback = finished_callback + _pocket_visual_callback_runs_on_interrupt = callback_runs_on_interrupt + _pocket_visual_midpoint_callback = midpoint_callback + _pocket_visual_midpoint_called = false + _pocket_visual_generation += 1 + var midpoint_generation: int = _pocket_visual_generation + _update_character_animation() + _schedule_pocket_visual_midpoint(midpoint_generation, animation_name) + + +func _is_moving_for_pocket_visual() -> bool: + var horizontal_velocity := Vector2(velocity.x, velocity.z) + return horizontal_velocity.length_squared() > 0.0025 + + +func _schedule_pocket_visual_midpoint( + generation: int, + animation_name: StringName, +) -> void: + if not _pocket_visual_midpoint_callback.is_valid(): + return + var animation: Animation = _character_animation_player.get_animation( + animation_name + ) + var midpoint_seconds: float = maxf(animation.length * 0.5, 0.0) + if midpoint_seconds > 0.0: + await get_tree().create_timer(midpoint_seconds, false).timeout + if ( + generation != _pocket_visual_generation + or not _pocket_visual_active + or _pocket_visual_animation != animation_name + ): + return + _apply_pocket_visual_midpoint() + + +func _apply_pocket_visual_midpoint() -> void: + if _pocket_visual_midpoint_called: + return + _pocket_visual_midpoint_called = true + var midpoint_callback: Callable = _pocket_visual_midpoint_callback + _pocket_visual_midpoint_callback = Callable() + if midpoint_callback.is_valid(): + midpoint_callback.call() + + +func _complete_pocket_visual() -> void: + if not _pocket_visual_active: + return + var finished_callback: Callable = _pocket_visual_finished_callback + _pocket_visual_active = false + _pocket_visual_target = PocketVisualTarget.NONE + _pocket_visual_animation = &"" + _pocket_visual_finished_callback = Callable() + _pocket_visual_callback_runs_on_interrupt = true + _pocket_visual_midpoint_callback = Callable() + _pocket_visual_midpoint_called = false + _pocket_visual_generation += 1 + if finished_callback.is_valid(): + finished_callback.call() + _update_character_animation() + + +func _cancel_pocket_visual() -> void: + _pocket_visual_active = false + _pocket_visual_target = PocketVisualTarget.NONE + _pocket_visual_animation = &"" + _pocket_visual_finished_callback = Callable() + _pocket_visual_callback_runs_on_interrupt = true + _pocket_visual_midpoint_callback = Callable() + _pocket_visual_midpoint_called = false + _pocket_visual_generation += 1 + + func get_cast_origin_position() -> Vector3: return _cast_origin.global_position @@ -761,6 +2219,10 @@ func get_cast_origin_position() -> Vector3: func begin_catch_showcase(fish_catch: FishCatchType) -> void: if fish_catch == null or not fish_catch.is_valid(): return + if _pocket_visual_target == PocketVisualTarget.CATCH_SHOWCASE: + _cancel_pocket_visual() + _showcase_animation_active = true + set_fishing_visual(false) _kill_showcase_camera_tween() _kill_showcase_restore_tween() _capture_showcase_camera_snapshot() @@ -780,12 +2242,15 @@ func begin_catch_showcase(fish_catch: FishCatchType) -> void: * catch_presentation_base_scale ) _catch_display.visible = _catch_sprite.texture != null + _update_character_animation() func begin_remote_catch_showcase(fish_catch: FishCatchType) -> void: if local_control_enabled or fish_catch == null or not fish_catch.is_valid(): return end_catch_showcase(Callable(), true) + _showcase_animation_active = true + set_fishing_visual(false) _showcase_rod_visibility = _fishing_rod.visible _showcase_rod_state_stored = true _fishing_rod.visible = false @@ -796,12 +2261,30 @@ func begin_remote_catch_showcase(fish_catch: FishCatchType) -> void: * catch_presentation_base_scale ) _catch_display.visible = _catch_sprite.texture != null + _update_character_animation() func end_catch_showcase( restored_callback: Callable = Callable(), immediate: bool = false, + pocket_completed: bool = false, ) -> void: + if immediate and _pocket_visual_target == PocketVisualTarget.CATCH_SHOWCASE: + _cancel_pocket_visual() + elif ( + not pocket_completed + and _showcase_animation_active + and _catch_display.visible + ): + _begin_pocket_visual( + PocketVisualTarget.CATCH_SHOWCASE, + true, + false, + end_catch_showcase.bind(restored_callback, false, true), + ) + return + _showcase_animation_active = false + _update_character_animation() _kill_showcase_turn_tween() _kill_showcase_camera_tween() _kill_showcase_restore_tween() @@ -972,7 +2455,10 @@ func _complete_showcase_restore( _camera_input_enabled = ( _showcase_camera_snapshot.camera_input_enabled ) - _camera_dragging = _showcase_camera_snapshot.camera_dragging + _set_camera_dragging( + _showcase_camera_snapshot.camera_dragging + and Input.is_action_pressed("camera_drag") + ) _showcase_camera_snapshot = null if restored_callback.is_valid(): restored_callback.call() @@ -990,7 +2476,7 @@ func _capture_showcase_camera_snapshot() -> void: _showcase_camera_snapshot.camera_input_enabled = _camera_input_enabled _showcase_camera_snapshot.camera_dragging = _camera_dragging _camera_input_enabled = false - _camera_dragging = false + _set_camera_dragging(false) func _begin_showcase_camera_transition() -> Vector3: diff --git a/player/player.tscn b/player/player.tscn index 2f7c0fb..43ab900 100644 --- a/player/player.tscn +++ b/player/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3] +[gd_scene load_steps=22 format=3] [ext_resource type="Script" path="res://player/player.gd" id="1_script"] [ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"] @@ -15,6 +15,7 @@ [ext_resource type="PackedScene" path="res://art/exported/characters/base/netfishing_base_character.glb" id="13_character"] [ext_resource type="Script" path="res://progression/player_art_unlocks.gd" id="14_art_unlocks"] [ext_resource type="Script" path="res://progression/player_experience.gd" id="15_experience"] +[ext_resource type="Script" path="res://player/sprint_dust_trail.gd" id="16_sprint_dust"] [sub_resource type="CapsuleShape3D" id="PlayerShape"] radius = 0.45 @@ -24,6 +25,17 @@ height = 1.8 material = ExtResource("12_blob_shadow") size = Vector2(1.15, 0.72) +[sub_resource type="Shader" id="SprintDustShader"] +code = "shader_type spatial;\nrender_mode blend_mix, depth_draw_never, cull_disabled, unshaded, shadows_disabled;\n\nuniform vec3 dust_color : source_color = vec3(0.61, 0.57, 0.53);\nvarying flat float puff_phase;\nvarying flat float puff_alpha;\n\nvoid vertex() {\n\tpuff_phase = INSTANCE_CUSTOM.x;\n\tpuff_alpha = INSTANCE_CUSTOM.w;\n\tVERTEX.xy *= INSTANCE_CUSTOM.yz;\n\tfloat puff_rotation = puff_phase * 6.28318530718;\n\tfloat rotation_sine = sin(puff_rotation);\n\tfloat rotation_cosine = cos(puff_rotation);\n\tVERTEX.xy = mat2(\n\t\tvec2(rotation_cosine, -rotation_sine),\n\t\tvec2(rotation_sine, rotation_cosine)\n\t) * VERTEX.xy;\n\tMODELVIEW_MATRIX = VIEW_MATRIX * mat4(\n\t\tINV_VIEW_MATRIX[0],\n\t\tINV_VIEW_MATRIX[1],\n\t\tINV_VIEW_MATRIX[2],\n\t\tMODEL_MATRIX[3]\n\t);\n}\n\nvoid fragment() {\n\tvec2 centered_uv = UV * 2.0 - 1.0;\n\tfloat angle = atan(centered_uv.y, centered_uv.x);\n\tfloat edge_wobble =\n\t\tsin(angle * 5.0 + puff_phase * 8.0) * 0.07\n\t\t+ sin(angle * 3.0 - puff_phase * 5.0) * 0.025;\n\tfloat radius = length(centered_uv);\n\tif (radius > 0.82 + edge_wobble) {\n\t\tdiscard;\n\t}\n\tALBEDO = dust_color;\n\tALPHA = puff_alpha;\n}\n" + +[sub_resource type="ShaderMaterial" id="SprintDustMaterial"] +render_priority = 1 +shader = SubResource("SprintDustShader") + +[sub_resource type="QuadMesh" id="SprintDustMesh"] +material = SubResource("SprintDustMaterial") +size = Vector2(1, 1) + [node name="Player" type="CharacterBody3D"] collision_layer = 2 collision_mask = 1 @@ -55,6 +67,12 @@ rotation_degrees = Vector3(-90, 0, 0) cast_shadow = 0 mesh = SubResource("GroundShadowMesh") +[node name="SprintDust" type="MultiMeshInstance3D" parent="."] +unique_name_in_owner = true +cast_shadow = 0 +script = ExtResource("16_sprint_dust") +puff_mesh = SubResource("SprintDustMesh") + [node name="CharacterRig" parent="Visuals" instance=ExtResource("13_character")] rotation_degrees = Vector3(0, 180, 0) @@ -109,49 +127,6 @@ script = ExtResource("14_art_unlocks") unique_name_in_owner = true script = ExtResource("15_experience") -[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"] -unique_name_in_owner = true -position = Vector3(0, 1.45, -1.15) - -[node name="CatchDisplay" type="Node3D" parent="Visuals/CatchDisplayAnchor"] -unique_name_in_owner = true -visible = false - -[node name="CatchSprite" type="Sprite3D" parent="Visuals/CatchDisplayAnchor/CatchDisplay"] -unique_name_in_owner = true -pixel_size = 0.012 -billboard = 1 -shaded = false -double_sided = true -texture_filter = 0 - -[node name="HeldFishAnchor" type="Marker3D" parent="Visuals"] -position = Vector3(0, 1.15, -0.9) - -[node name="HeldFishDisplay" type="Node3D" parent="Visuals/HeldFishAnchor"] -unique_name_in_owner = true -visible = false - -[node name="HeldFishSprite" type="Sprite3D" parent="Visuals/HeldFishAnchor/HeldFishDisplay"] -unique_name_in_owner = true -pixel_size = 0.012 -billboard = 1 -shaded = false -double_sided = true -texture_filter = 0 - -[node name="HeldArtKitAnchor" type="Marker3D" parent="Visuals"] -position = Vector3(0, 1.05, -0.72) - -[node name="HeldArtKitSprite" type="Sprite3D" parent="Visuals/HeldArtKitAnchor"] -unique_name_in_owner = true -visible = false -pixel_size = 0.008 -billboard = 1 -shaded = false -double_sided = true -texture_filter = 1 - [node name="CameraYaw" type="Node3D" parent="."] unique_name_in_owner = true position = Vector3(0, 1.35, 0) diff --git a/player/player_ground_shadow.gd b/player/player_ground_shadow.gd index 9a4bb6f..0efa948 100644 --- a/player/player_ground_shadow.gd +++ b/player/player_ground_shadow.gd @@ -54,7 +54,7 @@ func _physics_process(delta: float) -> void: _visual.transparency = 1.0 - _opacity -func _place_on_surface(position: Vector3, normal: Vector3) -> void: +func _place_on_surface(surface_position: Vector3, normal: Vector3) -> void: var tangent := Vector3.RIGHT - normal * Vector3.RIGHT.dot(normal) if tangent.length_squared() < 0.001: tangent = Vector3.FORWARD - normal * Vector3.FORWARD.dot(normal) @@ -62,5 +62,5 @@ func _place_on_surface(position: Vector3, normal: Vector3) -> void: var bitangent := tangent.cross(normal).normalized() _placement.global_transform = Transform3D( Basis(tangent, normal, bitangent), - position + normal * surface_offset, + surface_position + normal * surface_offset, ) diff --git a/player/player_visual_presenter.gd b/player/player_visual_presenter.gd index e75f791..7a1ae1c 100644 --- a/player/player_visual_presenter.gd +++ b/player/player_visual_presenter.gd @@ -2,6 +2,65 @@ class_name PlayerVisualPresenter extends RefCounted const PlayerScene: PackedScene = preload("res://player/player.tscn") +const FurPatternShader: Shader = preload("res://player/fur_pattern.gdshader") +const RUNTIME_MATERIAL_META: StringName = &"netfishing_runtime_material" +const RUNTIME_FUR_PATTERN_MATERIAL_META: StringName = ( + &"netfishing_runtime_fur_pattern_material" +) +const EAR_MESH_NAMES: Array[String] = [ + "ears_antlers_round", + "ears_bear", + "ears_bunny", + "ears_pointy_long", + "ears_pointy_short", + "ears_pointy_wide", +] +const TAIL_MESH_NAMES: Array[String] = [ + "tails_bear", + "tails_bunny", + "tails_cat", + "tails_fox", + "tails_gator", + "tails_pointy", +] + +static var _feature_uv_aspects: Dictionary = {} + + +static func feature_uv_aspect(feature_name: String) -> float: + if _feature_uv_aspects.has(feature_name): + return float(_feature_uv_aspects[feature_name]) + var visuals := instantiate_visuals() + var skeleton := visuals.find_child("Skeleton3D", true, false) as Skeleton3D + var aspects: Array[float] = [] + for head_name: String in ["head_round", "head_pointy"]: + var mesh_instance := skeleton.get_node_or_null( + "%s_%s" % [head_name, feature_name] + ) as MeshInstance3D + if mesh_instance == null or mesh_instance.mesh == null: + continue + var arrays := mesh_instance.mesh.surface_get_arrays(0) + var uv: PackedVector2Array = arrays[Mesh.ARRAY_TEX_UV] + if uv.is_empty(): + continue + var min_uv := Vector2(INF, INF) + var max_uv := Vector2(-INF, -INF) + for value: Vector2 in uv: + min_uv = min_uv.min(value) + max_uv = max_uv.max(value) + var size := max_uv - min_uv + if size.y > 0.001: + aspects.append(size.x / size.y) + visuals.free() + var result := 1.0 + if not aspects.is_empty(): + result = 0.0 + for aspect: float in aspects: + result += aspect + result /= aspects.size() + result = clampf(result, 0.35, 2.5) + _feature_uv_aspects[feature_name] = result + return result static func instantiate_visuals() -> Node3D: @@ -32,10 +91,228 @@ static func instantiate_visuals() -> Node3D: static func apply_appearance( - _visuals: Node3D, + visuals: Node3D, snapshot: Dictionary, ) -> void: - # Gameplay and preview deliberately share this presentation seam. Modular - # visual parts can be applied here without coupling Profile to Player. if not CharacterCustomizationCatalog.validate_snapshot(snapshot): return + # Gameplay and preview deliberately share this presentation seam. Keep all + # mesh selection here so profile previews and network avatars stay identical. + var character_scale: float = CharacterCustomizationCatalog.character_scale( + snapshot.get( + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ) + visuals.scale = Vector3.ONE * character_scale + var skeleton: Skeleton3D = visuals.find_child("Skeleton3D", true, false) as Skeleton3D + if skeleton == null: + return + + var species_id: String = CharacterCustomizationCatalog.canonical_option_id( + "species", str(snapshot.get("species", "round")) + ) + var head_id: String = "head_pointy" if species_id == "pointy" else "head_round" + var head_ids: Array[String] = ["head_pointy", "head_round"] + for head_name: String in head_ids: + var head: Node3D = skeleton.get_node_or_null(head_name) as Node3D + if head != null: + head.visible = head_name == head_id + for feature: String in ["eyes", "mouth", "nose"]: + var decal: Node3D = skeleton.get_node_or_null( + "%s_%s" % [head_name, feature] + ) as Node3D + if decal != null: + var feature_id := CharacterCustomizationCatalog.canonical_option_id( + feature, str(snapshot.get(feature, "none")) + ) + decal.visible = ( + head_name == head_id and feature_id != "none" + ) + + var selected_ears: String = CharacterCustomizationCatalog.canonical_option_id( + "ears", str(snapshot.get("ears", "none")) + ) + for ear_name: String in EAR_MESH_NAMES: + var ears: Node3D = skeleton.get_node_or_null(ear_name) as Node3D + if ears != null: + ears.visible = selected_ears == ear_name.trim_prefix("ears_") + var selected_tail: String = CharacterCustomizationCatalog.canonical_option_id( + "tail", str(snapshot.get("tail", "none")) + ) + for tail_name: String in TAIL_MESH_NAMES: + var tail: Node3D = skeleton.get_node_or_null(tail_name) as Node3D + if tail != null: + tail.visible = selected_tail == tail_name.trim_prefix("tails_") + + var fur_color: Color = CharacterCustomizationCatalog.option_color( + "fur_pattern", str(snapshot.get("fur_pattern", "white")) + ) + for fur_mesh_name: String in [ + "body_main", + "body_arms", + "head_pointy", + "head_round", + ]: + _set_fur_mesh(skeleton, fur_mesh_name, snapshot, fur_color) + for ear_name: String in EAR_MESH_NAMES: + _set_fur_mesh(skeleton, ear_name, snapshot, fur_color) + for tail_name: String in TAIL_MESH_NAMES: + _set_fur_mesh(skeleton, tail_name, snapshot, fur_color) + + var eye_id: String = CharacterCustomizationCatalog.canonical_option_id( + "eyes", str(snapshot.get("eyes", "simple_shine")) + ) + var mouth_id: String = CharacterCustomizationCatalog.canonical_option_id( + "mouth", str(snapshot.get("mouth", "three")) + ) + var nose_id: String = CharacterCustomizationCatalog.canonical_option_id( + "nose", str(snapshot.get("nose", "dog_round")) + ) + _set_feature_texture( + skeleton, + "eyes", + CharacterCustomizationCatalog.texture_for("eyes", eye_id), + ) + _set_feature_texture( + skeleton, + "mouth", + CharacterCustomizationCatalog.texture_for("mouth", mouth_id), + ) + _set_feature_texture( + skeleton, + "nose", + CharacterCustomizationCatalog.texture_for("nose", nose_id), + ) + + +static func _set_feature_texture( + skeleton: Skeleton3D, + feature_name: String, + texture: Texture2D, +) -> void: + for head_name: String in ["head_pointy", "head_round"]: + var mesh_instance: MeshInstance3D = skeleton.get_node_or_null( + "%s_%s" % [head_name, feature_name] + ) as MeshInstance3D + if texture == null: + if mesh_instance != null: + mesh_instance.visible = false + continue + var material: StandardMaterial3D = _runtime_material(mesh_instance) + if material != null: + # Feature plates are transparent geometry. The exported base model may + # have no preview texture, so enforce the runtime alpha mode when a + # drop-in PNG is assigned. + material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + material.albedo_color = Color.WHITE + material.albedo_texture = texture + + +static func _set_fur_mesh( + skeleton: Skeleton3D, + mesh_name: String, + snapshot: Dictionary, + base_color: Color, +) -> void: + var mesh_instance := skeleton.get_node_or_null(mesh_name) as MeshInstance3D + if mesh_instance == null: + return + var style_id := CharacterCustomizationCatalog.canonical_option_id( + CharacterCustomizationCatalog.FUR_STYLE_ID, + str(snapshot.get( + CharacterCustomizationCatalog.FUR_STYLE_ID, + CharacterCustomizationCatalog.DEFAULT_FUR_STYLE, + )), + ) + var pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture( + style_id, + mesh_name, + ) + if pattern_texture == null: + var solid_material := _runtime_material(mesh_instance) + if solid_material != null: + solid_material.albedo_color = base_color + mesh_instance.material_override = solid_material + return + + var pattern_material := _runtime_fur_pattern_material(mesh_instance) + pattern_material.set_shader_parameter("pattern_texture", pattern_texture) + pattern_material.set_shader_parameter("base_color", base_color) + pattern_material.set_shader_parameter( + "red_zone_color", + _fur_snapshot_color(snapshot, "fur_color_2", "cream"), + ) + pattern_material.set_shader_parameter( + "green_zone_color", + _fur_snapshot_color(snapshot, "fur_color_3", "brown"), + ) + pattern_material.set_shader_parameter( + "blue_zone_color", + _fur_snapshot_color(snapshot, "fur_color_4", "red"), + ) + mesh_instance.material_override = pattern_material + + +static func _fur_snapshot_color( + snapshot: Dictionary, + category_id: String, + default_id: String, +) -> Color: + return CharacterCustomizationCatalog.option_color( + category_id, + str(snapshot.get(category_id, default_id)), + ) + + +static func _runtime_material( + mesh_instance: MeshInstance3D, +) -> StandardMaterial3D: + if mesh_instance == null or mesh_instance.mesh == null: + return null + if mesh_instance.has_meta(RUNTIME_MATERIAL_META): + var runtime := mesh_instance.get_meta( + RUNTIME_MATERIAL_META + ) as StandardMaterial3D + _configure_matte_material(runtime) + return runtime + var source: StandardMaterial3D = mesh_instance.mesh.surface_get_material( + 0 + ) as StandardMaterial3D + if source == null: + return null + var material: StandardMaterial3D = source.duplicate() as StandardMaterial3D + _configure_matte_material(material) + mesh_instance.material_override = material + mesh_instance.set_meta(RUNTIME_MATERIAL_META, material) + return material + + +static func _runtime_fur_pattern_material( + mesh_instance: MeshInstance3D, +) -> ShaderMaterial: + if mesh_instance.has_meta(RUNTIME_FUR_PATTERN_MATERIAL_META): + return mesh_instance.get_meta( + RUNTIME_FUR_PATTERN_MATERIAL_META + ) as ShaderMaterial + var material := ShaderMaterial.new() + material.shader = FurPatternShader + mesh_instance.set_meta(RUNTIME_FUR_PATTERN_MATERIAL_META, material) + return material + + +static func _configure_matte_material(material: StandardMaterial3D) -> void: + if material == null: + return + material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST + # Character artwork is deliberately graphic and flat. Unshaded rendering + # keeps the authored fur and facial colors invariant across world weather, + # time of day, and the isolated Profile preview. + material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + material.metallic = 0.0 + material.metallic_specular = 0.0 + material.roughness = 1.0 + material.diffuse_mode = BaseMaterial3D.DIFFUSE_LAMBERT + material.anisotropy_enabled = false + material.clearcoat_enabled = false + material.rim_enabled = false diff --git a/player/sprint_dust_trail.gd b/player/sprint_dust_trail.gd new file mode 100644 index 0000000..124b6ae --- /dev/null +++ b/player/sprint_dust_trail.gd @@ -0,0 +1,249 @@ +class_name SprintDustTrail +extends MultiMeshInstance3D + +const PUFF_COUNT: int = 12 +const PUFF_SPACING: float = 0.46 +const PUFF_LIFETIME: float = 0.62 +const PUFF_GROW_TIME: float = 0.16 +const PUFF_FADE_TIME: float = 0.22 +const PUFF_BACK_OFFSET: float = 0.14 +const PUFF_SIDE_OFFSET: float = 0.11 +const PUFF_HEIGHT: float = 0.08 +const PUFF_MIN_SCALE: float = 0.22 + +@export var puff_mesh: Mesh + +var _puff_positions: Array[Vector3] = [] +var _puff_ages := PackedFloat32Array() +var _puff_widths := PackedFloat32Array() +var _puff_heights := PackedFloat32Array() +var _puff_phases := PackedFloat32Array() +var _next_puff: int = 0 +var _spawn_serial: int = 0 +var _foot_side: float = -1.0 +var _tracking_run: bool = false +var _previous_source_position: Vector3 +var _distance_since_puff: float = 0.0 + + +func _ready() -> void: + top_level = true + global_transform = Transform3D.IDENTITY + _initialize_pool() + + +func update_trail( + delta: float, + source_position: Vector3, + movement_velocity: Vector3, + is_running: bool, +) -> void: + _update_existing_puffs(delta) + custom_aabb = AABB( + source_position - Vector3(5.0, 2.0, 5.0), + Vector3(10.0, 5.0, 10.0), + ) + if not is_running: + _tracking_run = false + _distance_since_puff = 0.0 + return + + var flat_source := Vector3( + source_position.x, + source_position.y, + source_position.z, + ) + if not _tracking_run: + _tracking_run = true + _previous_source_position = flat_source + _distance_since_puff = 0.0 + return + + var segment := flat_source - _previous_source_position + segment.y = 0.0 + var remaining_distance: float = segment.length() + if remaining_distance <= 0.0001: + _previous_source_position = flat_source + return + + var segment_direction: Vector3 = segment / remaining_distance + var movement_direction := Vector3( + movement_velocity.x, + 0.0, + movement_velocity.z, + ).normalized() + if movement_direction.is_zero_approx(): + movement_direction = segment_direction + var cursor: Vector3 = _previous_source_position + var distance_to_next: float = PUFF_SPACING - _distance_since_puff + while remaining_distance >= distance_to_next: + cursor += segment_direction * distance_to_next + _spawn_puff(cursor, movement_direction) + remaining_distance -= distance_to_next + _distance_since_puff = 0.0 + distance_to_next = PUFF_SPACING + _distance_since_puff += remaining_distance + _previous_source_position = flat_source + + +func clear_trail() -> void: + _tracking_run = false + _distance_since_puff = 0.0 + for index: int in PUFF_COUNT: + _puff_ages[index] = PUFF_LIFETIME + _write_inactive_puff(index) + + +func emit_landing_burst( + source_position: Vector3, + facing_direction: Vector3, +) -> void: + var direction := Vector3( + facing_direction.x, + 0.0, + facing_direction.z, + ).normalized() + if direction.is_zero_approx(): + direction = Vector3.FORWARD + var first_direction: Vector3 = -direction + for puff_index: int in 5: + var spread_direction := first_direction.rotated( + Vector3.UP, + TAU * float(puff_index) / 5.0, + ) + var radius: float = lerpf( + 0.38, + 0.46, + _stable_variation(_spawn_serial + puff_index + 81), + ) + _spawn_puff_at( + source_position + + spread_direction * radius + + Vector3.UP * PUFF_HEIGHT, + 0.95, + ) + + +func get_active_puff_count() -> int: + var active_count: int = 0 + for age: float in _puff_ages: + if age < PUFF_LIFETIME: + active_count += 1 + return active_count + + +func get_first_active_puff_position() -> Vector3: + for index: int in PUFF_COUNT: + if _puff_ages[index] < PUFF_LIFETIME: + return _puff_positions[index] + return Vector3.INF + + +func _initialize_pool() -> void: + var pool := MultiMesh.new() + pool.transform_format = MultiMesh.TRANSFORM_3D + pool.use_custom_data = true + pool.instance_count = PUFF_COUNT + pool.mesh = puff_mesh + multimesh = pool + _puff_positions.resize(PUFF_COUNT) + _puff_ages.resize(PUFF_COUNT) + _puff_widths.resize(PUFF_COUNT) + _puff_heights.resize(PUFF_COUNT) + _puff_phases.resize(PUFF_COUNT) + clear_trail() + + +func _spawn_puff(source_position: Vector3, direction: Vector3) -> void: + var lateral := Vector3(-direction.z, 0.0, direction.x) + var position := ( + source_position + - direction * PUFF_BACK_OFFSET + + lateral * PUFF_SIDE_OFFSET * _foot_side + + Vector3.UP * PUFF_HEIGHT + ) + _spawn_puff_at(position) + _foot_side *= -1.0 + + +func _spawn_puff_at( + position: Vector3, + size_multiplier: float = 1.0, +) -> void: + var variation: float = _stable_variation(_spawn_serial) + var second_variation: float = _stable_variation(_spawn_serial + 37) + var index: int = _next_puff + _puff_positions[index] = position + _puff_ages[index] = 0.0 + _puff_widths[index] = ( + lerpf(0.34, 0.43, variation) * size_multiplier + ) + _puff_heights[index] = ( + lerpf(0.23, 0.30, second_variation) * size_multiplier + ) + _puff_phases[index] = variation + _write_puff(index, PUFF_MIN_SCALE, 1.0) + _next_puff = (_next_puff + 1) % PUFF_COUNT + _spawn_serial += 1 + + +func _update_existing_puffs(delta: float) -> void: + if multimesh == null: + return + for index: int in PUFF_COUNT: + var age: float = _puff_ages[index] + if age >= PUFF_LIFETIME: + continue + age += delta + _puff_ages[index] = age + if age >= PUFF_LIFETIME: + _write_inactive_puff(index) + continue + var visual_scale: float = 1.0 + if age < PUFF_GROW_TIME: + var grow_weight: float = smoothstep( + 0.0, + 1.0, + age / PUFF_GROW_TIME, + ) + visual_scale = lerpf(PUFF_MIN_SCALE, 1.0, grow_weight) + var visual_alpha: float = 1.0 + if age > PUFF_LIFETIME - PUFF_FADE_TIME: + var fade_weight: float = smoothstep( + 0.0, + 1.0, + (age - (PUFF_LIFETIME - PUFF_FADE_TIME)) + / PUFF_FADE_TIME, + ) + visual_alpha = 1.0 - fade_weight + _write_puff(index, visual_scale, visual_alpha) + + +func _write_puff( + index: int, + visual_scale: float, + visual_alpha: float, +) -> void: + var puff_transform := Transform3D.IDENTITY + puff_transform.origin = _puff_positions[index] + multimesh.set_instance_transform(index, puff_transform) + multimesh.set_instance_custom_data( + index, + Color( + _puff_phases[index], + _puff_widths[index] * visual_scale, + _puff_heights[index] * visual_scale, + visual_alpha, + ), + ) + + +func _write_inactive_puff(index: int) -> void: + var inactive_transform := Transform3D.IDENTITY + inactive_transform.origin = _puff_positions[index] + multimesh.set_instance_transform(index, inactive_transform) + multimesh.set_instance_custom_data(index, Color(0.0, 0.0, 0.0, 0.0)) + + +func _stable_variation(serial: int) -> float: + return fposmod(sin(float(serial + 1) * 12.9898) * 43758.5453, 1.0) diff --git a/player/sprint_dust_trail.gd.uid b/player/sprint_dust_trail.gd.uid new file mode 100644 index 0000000..c40239c --- /dev/null +++ b/player/sprint_dust_trail.gd.uid @@ -0,0 +1 @@ +uid://o2gi5oqunsp8 diff --git a/progression/character_customization_catalog.gd b/progression/character_customization_catalog.gd index fc34417..609f593 100644 --- a/progression/character_customization_catalog.gd +++ b/progression/character_customization_catalog.gd @@ -3,6 +3,7 @@ extends RefCounted const CATEGORY_IDS: PackedStringArray = [ "species", + "scale", "fur_pattern", "ears", "eyes", @@ -11,9 +12,28 @@ const CATEGORY_IDS: PackedStringArray = [ "tail", ] +# CATEGORY_IDS controls the visible Profile navigation. The additional fur +# fields are edited together on the existing fur page, but remain explicit in +# saves and signed multiplayer appearance snapshots. +const SNAPSHOT_IDS: PackedStringArray = [ + "species", + "scale", + "fur_pattern", + "fur_style", + "fur_color_2", + "fur_color_3", + "fur_color_4", + "ears", + "eyes", + "nose", + "mouth", + "tail", +] + const CATEGORY_LABELS: Dictionary = { - "species": "species", - "fur_pattern": "fur pattern", + "species": "head", + "scale": "size", + "fur_pattern": "fur", "ears": "ears", "eyes": "eyes", "nose": "nose", @@ -21,51 +41,452 @@ const CATEGORY_LABELS: Dictionary = { "tail": "tail", } -const OPTIONS: Dictionary = { - "species": [{"id": "default", "label": "default"}], - "fur_pattern": [{"id": "solid", "label": "solid"}], - "ears": [{"id": "default", "label": "default"}], - "eyes": [{"id": "default", "label": "default"}], - "nose": [{"id": "default", "label": "default"}], - "mouth": [{"id": "default", "label": "default"}], - "tail": [{"id": "none", "label": "none"}], +const SCALE_CATEGORY_ID: String = "scale" +const MIN_CHARACTER_SCALE: float = 0.5 +const MAX_CHARACTER_SCALE: float = 1.5 +const DEFAULT_CHARACTER_SCALE: float = 1.0 +const CHARACTER_SCALE_STEP: float = 0.05 +const FUR_STYLE_ID: String = "fur_style" +const FUR_COLOR_IDS: PackedStringArray = [ + "fur_pattern", + "fur_color_2", + "fur_color_3", + "fur_color_4", +] +const FUR_COLOR_LABELS: Dictionary = { + "fur_pattern": "primary", + "fur_color_2": "secondary", + "fur_color_3": "accent", + "fur_color_4": "detail", } +const DEFAULT_FUR_STYLE: String = "solid" + +const FEATURE_CATEGORIES: PackedStringArray = ["eyes", "nose", "mouth"] +const FEATURE_ASSET_ROOTS: Dictionary = { + "eyes": "res://art/exported/characters/faces/eyes", + "nose": "res://art/exported/characters/faces/noses", + "mouth": "res://art/exported/characters/faces/mouth", +} +const FEATURE_LABEL_OVERRIDES: Dictionary = { + "simple_shine": "simple shine", + "simple_shine_eyebrows": "simple shine + eyebrows", + "dog_round": "round", + "three": "three", +} +const FUR_COLOR_OPTIONS: Array = [ + {"id": "white", "label": "white", "color": Color("f2f0e8")}, + {"id": "ivory", "label": "ivory", "color": Color("f0e2c2")}, + {"id": "cream", "label": "cream", "color": Color("e6d39b")}, + {"id": "sand", "label": "sand", "color": Color("c6a66f")}, + {"id": "tan", "label": "tan", "color": Color("ad7c55")}, + {"id": "taupe", "label": "taupe", "color": Color("89756a")}, + {"id": "silver", "label": "silver", "color": Color("b8c3c1")}, + {"id": "gray", "label": "gray", "color": Color("819398")}, + {"id": "slate", "label": "slate", "color": Color("58686c")}, + {"id": "charcoal", "label": "charcoal", "color": Color("34444a")}, + {"id": "black", "label": "black", "color": Color("192126")}, + {"id": "dark_brown", "label": "dark brown", "color": Color("432a25")}, + {"id": "chocolate", "label": "chocolate", "color": Color("603b2c")}, + {"id": "brown", "label": "brown", "color": Color("7b4a32")}, + {"id": "chestnut", "label": "chestnut", "color": Color("98513d")}, + {"id": "ginger", "label": "ginger", "color": Color("d28742")}, + {"id": "orange", "label": "orange", "color": Color("c86c36")}, + {"id": "red", "label": "red", "color": Color("a9433f")}, + {"id": "pink", "label": "pink", "color": Color("d8899e")}, + {"id": "yellow", "label": "yellow", "color": Color("d8c545")}, + {"id": "gold", "label": "gold", "color": Color("c29a3d")}, + {"id": "sage", "label": "sage", "color": Color("8fa16b")}, + {"id": "green", "label": "green", "color": Color("6f913c")}, + {"id": "forest", "label": "forest", "color": Color("3f6945")}, + {"id": "aqua", "label": "aqua", "color": Color("69aeb0")}, + {"id": "teal", "label": "teal", "color": Color("3a8790")}, + {"id": "blue", "label": "blue", "color": Color("4d76a8")}, + {"id": "navy", "label": "navy", "color": Color("344b78")}, + {"id": "lavender", "label": "lavender", "color": Color("9a7eaa")}, + {"id": "purple", "label": "purple", "color": Color("76558f")}, +] +const OPTIONS: Dictionary = { + "species": [ + {"id": "round", "label": "round"}, + {"id": "pointy", "label": "pointy"}, + ], + # `fur_pattern` remains the primary color key for compatibility with + # existing appearance saves. The actual authored pattern has its own field. + "fur_pattern": FUR_COLOR_OPTIONS, + "fur_color_2": FUR_COLOR_OPTIONS, + "fur_color_3": FUR_COLOR_OPTIONS, + "fur_color_4": FUR_COLOR_OPTIONS, + "fur_style": [ + {"id": "solid", "label": "solid"}, + { + "id": "spots_bengal", + "label": "bengal spots", + "textures": { + "body_main": ( + "res://art/exported/characters/patterns/" + + "bengal/body_main_bengal.png" + ), + "body_arms": ( + "res://art/exported/characters/patterns/" + + "bengal/body_arms_bengal.png" + ), + "head_round": ( + "res://art/exported/characters/patterns/" + + "bengal/head_round_bengal.png" + ), + }, + }, + { + "id": "fox", + "label": "fox", + "textures": { + "body_main": ( + "res://art/exported/characters/patterns/" + + "fox/body_main_fox.png" + ), + "body_arms": ( + "res://art/exported/characters/patterns/" + + "fox/body_arms_fox.png" + ), + "head_pointy": ( + "res://art/exported/characters/patterns/" + + "fox/head_pointy_fox.png" + ), + "head_round": ( + "res://art/exported/characters/patterns/" + + "fox/head_round_fox.png" + ), + "tails_fox": ( + "res://art/exported/characters/patterns/" + + "fox/tails_fox_fox.png" + ), + }, + }, + ], + "ears": [ + {"id": "none", "label": "none"}, + {"id": "antlers_round", "label": "antlers"}, + {"id": "bear", "label": "bear"}, + {"id": "bunny", "label": "bunny"}, + {"id": "pointy_long", "label": "long"}, + {"id": "pointy_short", "label": "short"}, + {"id": "pointy_wide", "label": "wide"}, + ], + "eyes": [ + {"id": "simple_shine", "label": "simple shine"}, + { + "id": "simple_shine_eyebrows", + "label": "simple shine + eyebrows", + }, + ], + "nose": [{"id": "dog_round", "label": "round"}], + "mouth": [{"id": "three", "label": "three"}], + "tail": [ + {"id": "none", "label": "none"}, + {"id": "bear", "label": "bear"}, + {"id": "bunny", "label": "bunny"}, + {"id": "cat", "label": "cat"}, + {"id": "fox", "label": "fox"}, + {"id": "gator", "label": "gator"}, + {"id": "pointy", "label": "pointy"}, + ], +} + +const LEGACY_OPTION_ALIASES: Dictionary = { + "species": {"default": "round"}, + "fur_pattern": {"solid": "white"}, + "fur_style": {"bengal": "spots_bengal"}, + "ears": {"default": "none"}, + "tail": {"default": "none"}, + "eyes": {"default": "simple_shine"}, + "nose": {"default": "dog_round"}, + "mouth": {"default": "three"}, +} + +static var _feature_assets_ready: bool = false +static var _feature_options: Dictionary = {} +static var _feature_textures: Dictionary = {} +static var _fur_pattern_textures: Dictionary = {} static func default_snapshot() -> Dictionary: + _ensure_feature_assets() return { - "species": "default", - "fur_pattern": "solid", - "ears": "default", - "eyes": "default", - "nose": "default", - "mouth": "default", + "species": "round", + "scale": DEFAULT_CHARACTER_SCALE, + "fur_pattern": "white", + "fur_style": DEFAULT_FUR_STYLE, + "fur_color_2": "cream", + "fur_color_3": "brown", + "fur_color_4": "red", + "ears": "none", + "eyes": _default_feature_option("eyes", "simple_shine"), + "nose": _default_feature_option("nose", "dog_round"), + "mouth": _default_feature_option("mouth", "three"), "tail": "none", } +static func _default_feature_option( + category_id: String, + legacy_id: String, +) -> String: + var options: Array = _feature_options.get(category_id, []) as Array + for option: Dictionary in options: + if str(option.get("id", "")) == legacy_id: + return legacy_id + for option: Dictionary in options: + var option_id := str(option.get("id", "")) + if option_id != "none": + return option_id + return "none" + + static func options_for(category_id: String) -> Array: + if category_id in FEATURE_CATEGORIES: + _ensure_feature_assets() + return _feature_options.get(category_id, []) as Array return OPTIONS.get(category_id, []) +static func feature_option_groups(category_id: String) -> Array: + if category_id not in FEATURE_CATEGORIES: + return [] + var options: Array = options_for(category_id) + var option_ids: Array[String] = [] + for option: Dictionary in options: + option_ids.append(str(option.get("id", ""))) + var grouped_options: Dictionary = {} + var group_order: Array[String] = [] + for option: Dictionary in options: + var option_id := str(option.get("id", "")) + var group_id := _feature_variant_root(option_id, option_ids) + if not grouped_options.has(group_id): + grouped_options[group_id] = [] + group_order.append(group_id) + var group: Array = grouped_options[group_id] as Array + group.append(option) + grouped_options[group_id] = group + var result: Array = [] + for group_id: String in group_order: + result.append({ + "id": group_id, + "options": grouped_options[group_id], + }) + return result + + +static func _feature_variant_root( + option_id: String, + option_ids: Array[String], +) -> String: + if option_id == "none": + return option_id + # Prefer the shortest authored base option. This keeps nested names such as + # `beady_extra_shine` in the `beady` drawer rather than creating drawers + # inside drawers. + var authored_root := "" + for candidate_id: String in option_ids: + if candidate_id == "none": + continue + if ( + option_id != candidate_id + and not option_id.begins_with(candidate_id + "_") + ): + continue + var has_descendant := false + for descendant_id: String in option_ids: + if descendant_id.begins_with(candidate_id + "_"): + has_descendant = true + break + if ( + has_descendant + and ( + authored_root.is_empty() + or candidate_id.length() < authored_root.length() + ) + ): + authored_root = candidate_id + if not authored_root.is_empty(): + return authored_root + + # Some families have no unqualified base asset. Group those siblings by + # their leading name segment, making the sorted first asset representative. + var separator_index := option_id.find("_") + if separator_index <= 0: + return option_id + var leading_segment := option_id.substr(0, separator_index) + var sibling_count := 0 + for candidate_id: String in option_ids: + if candidate_id.begins_with(leading_segment + "_"): + sibling_count += 1 + if sibling_count >= 2: + return leading_segment + return option_id + + +static func texture_for(category_id: String, option_id: String) -> Texture2D: + if category_id not in FEATURE_CATEGORIES: + return null + _ensure_feature_assets() + var canonical_id := canonical_option_id(category_id, option_id) + var textures: Dictionary = _feature_textures.get(category_id, {}) as Dictionary + return textures.get(canonical_id) as Texture2D + + +static func refresh_feature_assets() -> void: + _feature_assets_ready = false + _feature_options.clear() + _feature_textures.clear() + _ensure_feature_assets() + + +static func _ensure_feature_assets() -> void: + if _feature_assets_ready: + return + _feature_assets_ready = true + for category_id: String in FEATURE_CATEGORIES: + var options: Array = [{"id": "none", "label": "none"}] + var textures: Dictionary = {} + var root_path := str(FEATURE_ASSET_ROOTS[category_id]) + var directory := DirAccess.open(root_path) + if directory != null: + var files := directory.get_files() + files.sort() + var normalized_paths: Dictionary = {} + for file_name: String in files: + var normalized_file_name := _canonical_feature_filename(file_name) + if ( + normalized_file_name.is_empty() + or normalized_paths.has(normalized_file_name) + ): + continue + normalized_paths[normalized_file_name] = true + var option_id := _feature_id_from_filename( + category_id, normalized_file_name + ) + if option_id.is_empty() or textures.has(option_id): + continue + var resource_path := root_path.path_join(normalized_file_name) + var texture := ResourceLoader.load(resource_path) as Texture2D + if texture == null: + push_warning( + "Ignoring facial feature without a loadable texture: " + + resource_path + ) + continue + textures[option_id] = texture + options.append({ + "id": option_id, + "label": _feature_label(option_id), + }) + + _feature_options[category_id] = options + _feature_textures[category_id] = textures + + +static func _canonical_feature_filename(file_name: String) -> String: + var lower_name := file_name.to_lower() + for sidecar_suffix: String in [".import", ".remap"]: + if lower_name.ends_with(".png" + sidecar_suffix): + return file_name.substr( + 0, file_name.length() - sidecar_suffix.length() + ) + if lower_name.ends_with(".png"): + return file_name + return "" + + +static func _feature_id_from_filename( + category_id: String, + file_name: String, +) -> String: + var option_id := file_name.get_basename().to_lower() + var prefix := category_id + "_" + if option_id.begins_with(prefix): + option_id = option_id.trim_prefix(prefix) + option_id = option_id.replace("-", "_") + option_id = option_id.replace(" ", "_") + option_id = option_id.replace(".", "_") + while option_id.contains("__"): + option_id = option_id.replace("__", "_") + while option_id.begins_with("_"): + option_id = option_id.substr(1) + while option_id.ends_with("_"): + option_id = option_id.substr(0, option_id.length() - 1) + return option_id + + +static func _feature_label(option_id: String) -> String: + if FEATURE_LABEL_OVERRIDES.has(option_id): + return str(FEATURE_LABEL_OVERRIDES[option_id]) + return option_id.replace("_", " ") + + static func category_label(category_id: String) -> String: return str(CATEGORY_LABELS.get(category_id, category_id)) static func is_valid_option(category_id: String, option_id: String) -> bool: + option_id = canonical_option_id(category_id, option_id) for option: Dictionary in options_for(category_id): if str(option.get("id", "")) == option_id: return true return false +static func canonical_option_id(category_id: String, option_id: String) -> String: + var aliases: Dictionary = LEGACY_OPTION_ALIASES.get(category_id, {}) + return str(aliases.get(option_id, option_id)) + + +static func option_color(category_id: String, option_id: String) -> Color: + var canonical_id: String = canonical_option_id(category_id, option_id) + for option: Dictionary in options_for(category_id): + if str(option.get("id", "")) == canonical_id: + return option.get("color", Color.WHITE) as Color + return Color.WHITE + + +static func fur_color_label(category_id: String) -> String: + return str(FUR_COLOR_LABELS.get(category_id, category_id)) + + +static func fur_pattern_texture( + option_id: String, + component_id: String = "body_main", +) -> Texture2D: + var canonical_id := canonical_option_id(FUR_STYLE_ID, option_id) + if canonical_id == DEFAULT_FUR_STYLE: + return null + var cache_id := "%s:%s" % [canonical_id, component_id] + if _fur_pattern_textures.has(cache_id): + return _fur_pattern_textures[cache_id] as Texture2D + for option: Dictionary in options_for(FUR_STYLE_ID): + if str(option.get("id", "")) != canonical_id: + continue + var textures := option.get("textures", {}) as Dictionary + var resource_path := str(textures.get(component_id, "")) + if resource_path.is_empty(): + return null + var texture := ResourceLoader.load(resource_path) as Texture2D + if texture != null: + _fur_pattern_textures[cache_id] = texture + return texture + return null + + static func validate_snapshot(value: Variant) -> bool: if typeof(value) != TYPE_DICTIONARY: return false var snapshot: Dictionary = value - if snapshot.size() != CATEGORY_IDS.size(): + if snapshot.size() != SNAPSHOT_IDS.size(): return false - for category_id: String in CATEGORY_IDS: + for category_id: String in SNAPSHOT_IDS: + if category_id == SCALE_CATEGORY_ID: + if not is_valid_character_scale(snapshot.get(category_id)): + return false + continue if ( typeof(snapshot.get(category_id)) != TYPE_STRING or not is_valid_option(category_id, str(snapshot[category_id])) @@ -79,8 +500,44 @@ static func sanitized_snapshot(value: Variant) -> Dictionary: if typeof(value) != TYPE_DICTIONARY: return result var snapshot: Dictionary = value - for category_id: String in CATEGORY_IDS: - var option_id := str(snapshot.get(category_id, "")) + for category_id: String in SNAPSHOT_IDS: + if category_id == SCALE_CATEGORY_ID: + result[category_id] = character_scale( + snapshot.get(category_id, DEFAULT_CHARACTER_SCALE) + ) + continue + var option_id: String = canonical_option_id( + category_id, str(snapshot.get(category_id, "")) + ) if is_valid_option(category_id, option_id): result[category_id] = option_id return result + + +static func character_scale(value: Variant) -> float: + if typeof(value) not in [TYPE_FLOAT, TYPE_INT]: + return DEFAULT_CHARACTER_SCALE + var number: float = float(value) + if not is_finite(number): + return DEFAULT_CHARACTER_SCALE + return clampf( + snappedf(number, CHARACTER_SCALE_STEP), + MIN_CHARACTER_SCALE, + MAX_CHARACTER_SCALE, + ) + + +static func is_valid_character_scale(value: Variant) -> bool: + if typeof(value) not in [TYPE_FLOAT, TYPE_INT]: + return false + var number: float = float(value) + return ( + is_finite(number) + and number >= MIN_CHARACTER_SCALE + and number <= MAX_CHARACTER_SCALE + and is_equal_approx(number, character_scale(number)) + ) + + +static func character_scale_percent(value: Variant) -> int: + return roundi(character_scale(value) * 100.0) diff --git a/progression/player_item_effects.gd b/progression/player_item_effects.gd index d7542fc..1a73c43 100644 --- a/progression/player_item_effects.gd +++ b/progression/player_item_effects.gd @@ -10,6 +10,8 @@ const COFFEE_ID: StringName = &"coffee" const ENERGY_DRINK_ID: StringName = &"energy_drink" const SNACK_ID: StringName = &"snack" const FISH_FINDER_ID: StringName = &"fish_finder" +const BATTERIES_ID: StringName = &"batteries" +const FISH_FINDER_DEAD_MESSAGE: String = "hmm... batteries are dead..." const COFFEE_DURATION: float = 90.0 const ENERGY_DRINK_DURATION: float = 90.0 const SNACK_DURATION: float = 60.0 @@ -45,16 +47,35 @@ func use_consumable( item: ItemDataType, bag: PlayerBagType, ) -> bool: + return use_item(item, bag) + + +func use_item( + item: ItemDataType, + bag: PlayerBagType, +) -> bool: + var is_fish_finder: bool = ( + item != null + and item.item_id == FISH_FINDER_ID + and item.category == ItemDataType.Category.TOOL + and item.equippable + ) if ( item == null or bag == null - or item.category != ItemDataType.Category.CONSUMABLE + or ( + item.category != ItemDataType.Category.CONSUMABLE + and not is_fish_finder + ) or not item.usable or not _remaining.has(item.item_id) or not bag.owns_item(item.item_id) ): return false - if not bag.remove_item(item.item_id, 1): + var consumed_item_id: StringName = ( + BATTERIES_ID if is_fish_finder else item.item_id + ) + if not bag.remove_item(consumed_item_id, 1): return false _remaining[item.item_id] = _get_duration(item.item_id) effects_changed.emit() diff --git a/project.godot b/project.godot index 1931200..1745ecc 100644 --- a/project.godot +++ b/project.godot @@ -11,21 +11,43 @@ config_version=5 [application] config/name="NETFISHING" -config/version="0.3.5-prealpha" +config/version="0.7.1-alpha" run/main_scene="res://main/main.tscn" config/features=PackedStringArray("4.7", "GL Compatibility") -config/icon="res://icon.svg" +config/icon="res://art/exported/system_icons/netfishing_256.png" + +[autoload] + +TextureSamplingPolicy="*res://main/texture_sampling_policy.gd" [display] window/stretch/aspect="expand" +[editor_plugins] + +enabled=PackedStringArray("res://addons/netfishing_shoreline_baker/plugin.cfg") + [gui] theme/custom="res://ui/game_theme.tres" [input] +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +] +} move_forward={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) @@ -53,11 +75,13 @@ move_right={ sprint={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":7,"pressure":0.0,"pressed":false,"script":null) ] } jump={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) ] } sneak={ @@ -73,11 +97,13 @@ slow_walk={ interact={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":3,"pressure":0.0,"pressed":false,"script":null) ] } fish_primary={ "deadzone": 0.2, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":32,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":10,"pressure":0.0,"pressed":false,"script":null) ] } camera_drag={ @@ -140,14 +166,51 @@ hotbar_9={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +hotbar_previous={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +] +} +hotbar_next={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +] +} open_backpack={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":false,"script":null) +] +} +open_system_menu={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":6,"pressure":0.0,"pressed":false,"script":null) ] } open_emotes={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +] +} +character_call={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":71,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +open_quick_actions={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +] +} +open_chat={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":4,"pressure":0.0,"pressed":false,"script":null) +] +} +focus_gameplay={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":9,"pressure":0.0,"pressed":false,"script":null) ] } open_tacklebox={ @@ -186,6 +249,10 @@ alternate_fishing={ ] } +[network] + +discovery/base_url="https://discovery.netfishing.org" + [physics] 3d/physics_engine="Jolt Physics" @@ -195,3 +262,4 @@ alternate_fishing={ rendering_device/driver.windows="d3d12" renderer/rendering_method="gl_compatibility" renderer/rendering_method.mobile="gl_compatibility" +textures/vram_compression/import_etc2_astc=true diff --git a/save/player_save_manager.gd b/save/player_save_manager.gd index 72f7676..57e355d 100644 --- a/save/player_save_manager.gd +++ b/save/player_save_manager.gd @@ -25,8 +25,12 @@ const PlayerExperienceType = preload( "res://progression/player_experience.gd" ) const WorldTimeServiceType = preload("res://world/world_time_service.gd") +const WorldWeatherServiceType = preload( + "res://world/world_weather_service.gd" +) +const PlayerJobServiceType = preload("res://jobs/player_job_service.gd") -const SAVE_VERSION: int = 6 +const SAVE_VERSION: int = 7 const BASIC_ROD_ID: StringName = &"basic_fishing_rod" const MAX_SAFE_BALANCE: int = 1000000000000 @@ -39,6 +43,7 @@ class LoadSnapshot: var wallet_balance: int = 0 var next_catch_sequence: int = 1 var bag_items: Array[OwnedItemType] = [] + var unlocked_bait_ids: Array[StringName] = [] var hotbar_slots: Array[StringName] = [] var fish_hotbar_slots: Array[StringName] = [] var selected_hotbar_slot: int = 0 @@ -48,6 +53,14 @@ class LoadSnapshot: var art_unlock_mask: int = 0 var total_experience: int = 0 var world_time_hours: float = WorldTimeServiceType.DEFAULT_START_HOUR + var has_world_weather_state: bool = false + var world_weather: WorldWeatherServiceType.Weather = ( + WorldWeatherServiceType.DEFAULT_WEATHER + ) + var world_weather_seconds_remaining: float = ( + WorldWeatherServiceType.SUNNY_DURATION_RANGE.x + ) + var jobs_data: Dictionary = PlayerJobServiceType.default_save_data() @export_range(0.05, 5.0, 0.05) var autosave_delay: float = 0.5 @@ -64,6 +77,8 @@ var _cooler_capacity: PlayerCoolerCapacityType var _art_unlocks: PlayerArtUnlocksType var _experience: PlayerExperienceType var _world_time: WorldTimeServiceType +var _world_weather: WorldWeatherServiceType +var _jobs: PlayerJobServiceType var _autosave_timer: Timer var _is_configured: bool = false var _is_restoring: bool = false @@ -108,6 +123,8 @@ func setup( art_unlocks: PlayerArtUnlocksType, experience: PlayerExperienceType, world_time: WorldTimeServiceType, + world_weather: WorldWeatherServiceType, + jobs: PlayerJobServiceType, ) -> void: _inventory = inventory _collection_log = collection_log @@ -121,6 +138,8 @@ func setup( _art_unlocks = art_unlocks _experience = experience _world_time = world_time + _world_weather = world_weather + _jobs = jobs _is_configured = ( _inventory != null and _collection_log != null @@ -134,6 +153,8 @@ func setup( and _art_unlocks != null and _experience != null and _world_time != null + and _world_weather != null + and _jobs != null ) if not _is_configured: push_error("PlayerSaveManager setup is missing required references.") @@ -172,6 +193,8 @@ func setup( _on_experience_changed ): _experience.experience_changed.connect(_on_experience_changed) + if not _jobs.changed.is_connected(_mark_dirty): + _jobs.changed.connect(_mark_dirty) func load_player_data() -> bool: @@ -236,7 +259,10 @@ func load_player_data() -> bool: var wallet_restored: bool = _wallet.restore_balance( snapshot.wallet_balance ) - var bag_restored: bool = _bag.replace_all_items(snapshot.bag_items) + var bag_restored: bool = ( + _bag.replace_all_items(snapshot.bag_items) + and _bag.replace_unlocked_bait_ids(snapshot.unlocked_bait_ids) + ) var hotbar_restored: bool = _hotbar.replace_state( snapshot.hotbar_slots, snapshot.selected_hotbar_slot, @@ -258,6 +284,13 @@ func load_player_data() -> bool: var world_time_restored: bool = ( _world_time.restore_persistent_time_hours(snapshot.world_time_hours) ) + var world_weather_restored: bool = true + if snapshot.has_world_weather_state: + world_weather_restored = _world_weather.restore_persistent_state( + snapshot.world_weather, + snapshot.world_weather_seconds_remaining, + ) + var jobs_restored: bool = _jobs.restore_from_save_data(snapshot.jobs_data) _is_restoring = false if ( not inventory_restored @@ -270,6 +303,8 @@ func load_player_data() -> bool: or not art_restored or not experience_restored or not world_time_restored + or not world_weather_restored + or not jobs_restored ): push_error("Validated player save could not be restored.") return false @@ -461,6 +496,12 @@ func _build_save_dictionary() -> Dictionary: if owned == null or not owned.is_valid(): return {} serialized_items.append(owned.to_save_dict()) + var serialized_unlocked_baits: Array[String] = [] + for item_id: StringName in _bag.get_unlocked_bait_ids(): + var item_data = _item_catalog.get_item_by_id(item_id) + if item_data == null or not item_data.is_valid() or not item_data.is_bait(): + return {} + serialized_unlocked_baits.append(String(item_id)) var serialized_slots: Array[String] = [] for item_id: StringName in _hotbar.get_slots(): serialized_slots.append(String(item_id)) @@ -493,6 +534,7 @@ func _build_save_dictionary() -> Dictionary: }, "bag": { "items": serialized_items, + "unlocked_bait_ids": serialized_unlocked_baits, }, "hotbar": { "selected_slot": _hotbar.get_selected_slot(), @@ -505,7 +547,12 @@ func _build_save_dictionary() -> Dictionary: "experience": _experience.to_save_data(), "world": { "time_hours": _world_time.get_persistent_time_hours(), + "weather": int(_world_weather.get_persistent_weather()), + "weather_seconds_remaining": ( + _world_weather.get_persistent_seconds_remaining() + ), }, + "jobs": _jobs.to_save_data(), } @@ -517,6 +564,7 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot: or typeof(save_data.get("bag")) != TYPE_DICTIONARY or typeof(save_data.get("hotbar")) != TYPE_DICTIONARY or typeof(save_data.get("experience")) != TYPE_DICTIONARY + or typeof(save_data.get("jobs")) != TYPE_DICTIONARY ): return null var wallet_data: Dictionary = save_data["wallet"] @@ -525,6 +573,7 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot: var bag_data: Dictionary = save_data["bag"] var hotbar_data: Dictionary = save_data["hotbar"] var experience_data: Dictionary = save_data["experience"] + var jobs_data: Dictionary = save_data["jobs"] var world_data: Dictionary = {} if typeof(save_data.get("world")) == TYPE_DICTIONARY: world_data = save_data["world"] @@ -551,6 +600,10 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot: or typeof(inventory_data.get("catches")) != TYPE_ARRAY or not inventory_data.has("next_catch_sequence") or typeof(bag_data.get("items")) != TYPE_ARRAY + or ( + bag_data.has("unlocked_bait_ids") + and typeof(bag_data.get("unlocked_bait_ids")) != TYPE_ARRAY + ) or typeof(hotbar_data.get("slots")) != TYPE_ARRAY or not hotbar_data.has("selected_slot") or not experience_data.has("total_experience") @@ -579,12 +632,40 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot: ) if snapshot.total_experience < 0: return null + if not PlayerJobServiceType.validate_save_data(jobs_data): + return null + snapshot.jobs_data = jobs_data.duplicate(true) if world_data.has("time_hours"): snapshot.world_time_hours = _read_world_time_hours( world_data["time_hours"] ) if snapshot.world_time_hours < 0.0: return null + var has_weather: bool = world_data.has("weather") + var has_weather_seconds: bool = world_data.has( + "weather_seconds_remaining" + ) + if has_weather != has_weather_seconds: + return null + if has_weather: + var weather_value: int = _read_integer( + world_data["weather"], + -1, + WorldWeatherServiceType.Weather.FOGGY, + ) + var weather_seconds: float = _read_world_weather_seconds( + world_data["weather_seconds_remaining"] + ) + if ( + not WorldWeatherServiceType.is_valid_weather(weather_value) + or weather_seconds < 0.0 + ): + return null + snapshot.has_world_weather_state = true + snapshot.world_weather = ( + weather_value as WorldWeatherServiceType.Weather + ) + snapshot.world_weather_seconds_remaining = weather_seconds var discovered_values: Array = collection_data["discovered_fish_ids"] var seen_discoveries: Dictionary[StringName, bool] = {} for value: Variant in discovered_values: @@ -715,6 +796,34 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot: seen_items[item_id] = true snapshot.bag_items.append(owned) + var seen_unlocked_baits: Dictionary[StringName, bool] = {} + if bag_data.has("unlocked_bait_ids"): + var unlocked_bait_values: Array = bag_data["unlocked_bait_ids"] + for value: Variant in unlocked_bait_values: + if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]: + continue + var bait_id: StringName = StringName(str(value)) + var bait_data = _item_catalog.get_item_by_id(bait_id) + if ( + bait_id.is_empty() + or seen_unlocked_baits.has(bait_id) + or bait_data == null + or not bait_data.is_valid() + or not bait_data.is_bait() + ): + continue + seen_unlocked_baits[bait_id] = true + snapshot.unlocked_bait_ids.append(bait_id) + for owned: OwnedItemType in snapshot.bag_items: + var owned_item_data = _item_catalog.get_item_by_id(owned.item_id) + if ( + owned_item_data != null + and owned_item_data.is_bait() + and not seen_unlocked_baits.has(owned.item_id) + ): + seen_unlocked_baits[owned.item_id] = true + snapshot.unlocked_bait_ids.append(owned.item_id) + var slot_values: Array = hotbar_data["slots"] snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT) snapshot.hotbar_slots.fill(StringName()) @@ -806,6 +915,8 @@ func _migrate_save( migrated = _migrate_version_4_to_5(migrated) 5: migrated = _migrate_version_5_to_6(migrated) + 6: + migrated = _migrate_version_6_to_7(migrated) _: return {} if migrated.is_empty(): @@ -915,6 +1026,15 @@ func _migrate_version_5_to_6(data: Dictionary) -> Dictionary: return migrated +func _migrate_version_6_to_7(data: Dictionary) -> Dictionary: + var migrated: Dictionary = data.duplicate(true) + migrated["save_version"] = 7 + # Historical catch and sale totals cannot be reconstructed honestly from + # current inventory. New cumulative counters begin at feature introduction. + migrated["jobs"] = PlayerJobServiceType.default_save_data() + return migrated + + func _mark_dirty() -> void: if ( _is_restoring @@ -1021,6 +1141,8 @@ func _restore_defaults() -> void: ) _wallet.restore_balance(0) _bag.replace_all_items(default_items) + var default_unlocked_baits: Array[StringName] = [] + _bag.replace_unlocked_bait_ids(default_unlocked_baits) _hotbar.replace_state(default_slots, 0) _fishing_upgrades.reset_to_defaults() _cooler_capacity.reset_to_defaults() @@ -1029,6 +1151,8 @@ func _restore_defaults() -> void: _world_time.restore_persistent_time_hours( WorldTimeServiceType.DEFAULT_START_HOUR ) + _world_weather.reset_persistent_state() + _jobs.reset_to_defaults() _is_restoring = false _is_dirty = false @@ -1067,6 +1191,19 @@ func _read_world_time_hours(value: Variant) -> float: return time_hours +func _read_world_weather_seconds(value: Variant) -> float: + if typeof(value) not in [TYPE_FLOAT, TYPE_INT]: + return -1.0 + var seconds_remaining: float = float(value) + if ( + not is_finite(seconds_remaining) + or seconds_remaining < 0.0 + or seconds_remaining > WorldWeatherServiceType.MAX_PERSISTED_SECONDS + ): + return -1.0 + return seconds_remaining + + func _read_upgrade_level( value: Variant, maximum_level: int, diff --git a/scripts/build_android_debug.sh b/scripts/build_android_debug.sh new file mode 100755 index 0000000..8f856f8 --- /dev/null +++ b/scripts/build_android_debug.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -euo pipefail + +readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" +readonly OUTPUT_PATH="${PROJECT_ROOT}/builds/android/NETfishing-debug.apk" +readonly GODOT_BIN="${GODOT_BIN:-godot}" + +mkdir -p -- "$(dirname -- "${OUTPUT_PATH}")" + +"${GODOT_BIN}" \ + --headless \ + --path "${PROJECT_ROOT}" \ + --export-debug "Android" \ + "${OUTPUT_PATH}" + +test -s "${OUTPUT_PATH}" +sha256sum "${OUTPUT_PATH}" diff --git a/scripts/build_playtest.sh b/scripts/build_playtest.sh index de86ce4..7749b3d 100755 --- a/scripts/build_playtest.sh +++ b/scripts/build_playtest.sh @@ -4,12 +4,14 @@ set -euo pipefail readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" -readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.3.5-prealpha" +readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.7.1-alpha" readonly WINDOWS_DIR="${BUILD_ROOT}/windows-x86_64" readonly LINUX_DIR="${BUILD_ROOT}/linux-x86_64" -readonly README_SOURCE="${PROJECT_ROOT}/playtest/README-PLAYTEST.txt" -readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.3.5-prealpha-windows-x86_64.zip" -readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.3.5-prealpha-linux-x86_64.zip" +readonly README_SOURCE="${PROJECT_ROOT}/docs/README-PLAYTEST.txt" +readonly SOURCE_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)" +readonly SOURCE_URL="https://forge.makearmy.io/woofmeow/netfishing" +readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.7.1-alpha-windows-x86_64.zip" +readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.7.1-alpha-linux-x86_64.zip" readonly GODOT_BIN="${GODOT_BIN:-godot}" if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then @@ -52,6 +54,38 @@ chmod +x -- "${LINUX_DIR}/NETfishing.x86_64" cp -- "${README_SOURCE}" "${WINDOWS_DIR}/README-PLAYTEST.txt" cp -- "${README_SOURCE}" "${LINUX_DIR}/README-PLAYTEST.txt" +stage_licenses() { + local destination="$1/licenses" + mkdir -p -- "${destination}" + install -m 0644 "${PROJECT_ROOT}/LICENSE" \ + "${destination}/GPL-3.0-or-later.txt" + install -m 0644 "${PROJECT_ROOT}/ASSET-LICENSE.md" \ + "${destination}/NETFISHING-ASSET-LICENSE.md" + install -m 0644 "${PROJECT_ROOT}/docs/ATTRIBUTION.md" \ + "${destination}/ATTRIBUTION.md" + install -m 0644 "${PROJECT_ROOT}/TRADEMARKS.md" \ + "${destination}/TRADEMARKS.md" + install -m 0644 "${PROJECT_ROOT}/ui/fonts/Tuffy-LICENSE.txt" \ + "${destination}/Tuffy-LICENSE.txt" + install -m 0644 "${PROJECT_ROOT}/ui/fonts/Seattle-Avenue-LICENSE.txt" \ + "${destination}/Seattle-Avenue-LICENSE.txt" + cat >"${destination}/SOURCE-CODE.md" <&2 + exit 2 +fi + +readonly PROJECT_VERSION="$( + sed -n 's/^config\/version="\([^"]*\)"/\1/p' "${PROJECT_ROOT}/project.godot" +)" +if [[ -z "${PROJECT_VERSION}" ]]; then + echo "Could not read application/config/version from project.godot." >&2 + exit 1 +fi + +readonly RELEASE_TAG="v${PROJECT_VERSION}" +readonly RELEASE_ROOT="${PROJECT_ROOT}/builds/${RELEASE_TAG}" +readonly ARM64_ROOT="${RELEASE_ROOT}/linux-arm64" +readonly ARM64_EXECUTABLE="${ARM64_ROOT}/NETfishing.arm64" +readonly ARM64_PCK="${ARM64_ROOT}/NETfishing.pck" +readonly STAGE_ROOT="${RELEASE_ROOT}/portmaster-stage" +readonly GAME_ROOT="${STAGE_ROOT}/netfishing" +readonly ARCHIVE="${RELEASE_ROOT}/netfishing.zip" +readonly LEGACY_ARCHIVE="${RELEASE_ROOT}/netfishing-${RELEASE_TAG}-portmaster-arm64.zip" + +readonly HEAD_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)" +readonly TAG_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse "${RELEASE_TAG}^{commit}")" +if [[ "${HEAD_COMMIT}" != "${TAG_COMMIT}" ]]; then + echo "HEAD does not match ${RELEASE_TAG}. Refusing to package mutable source." >&2 + exit 1 +fi +if [[ "${NETFISHING_ALLOW_DIRTY_RELEASE:-0}" != "1" ]]; then + dirty_tree=0 + git -C "${PROJECT_ROOT}" diff --quiet || dirty_tree=1 + git -C "${PROJECT_ROOT}" diff --cached --quiet || dirty_tree=1 + if [[ -n "$(git -C "${PROJECT_ROOT}" status --porcelain --untracked-files=normal)" ]]; then + dirty_tree=1 + fi + if [[ ${dirty_tree} -ne 0 ]]; then + echo "The repository is not clean. Refusing to build a release package." >&2 + exit 1 + fi +fi + +for command_name in git file zip unzip sha256sum; do + command -v "${command_name}" >/dev/null 2>&1 || { + echo "Required command not found: ${command_name}" >&2 + exit 1 + } +done + +mkdir -p -- "${ARM64_ROOT}" "${RELEASE_ROOT}" +if [[ ${PACKAGE_ONLY} -eq 0 ]]; then + rm -f -- "${ARM64_EXECUTABLE}" "${ARM64_PCK}" + "${GODOT_BIN}" \ + --headless \ + --path "${PROJECT_ROOT}" \ + --export-release "Linux ARM64" +fi + +test -s "${ARM64_EXECUTABLE}" +test -s "${ARM64_PCK}" +file "${ARM64_EXECUTABLE}" | grep -q "ARM aarch64" + +rm -rf -- "${STAGE_ROOT}" +rm -f -- "${ARCHIVE}" "${LEGACY_ARCHIVE}" +mkdir -p -- "${GAME_ROOT}/licenses" + +install -m 0755 "${TEMPLATE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh" +install -m 0644 "${TEMPLATE_ROOT}/port.json" "${STAGE_ROOT}/port.json" +install -m 0755 "${ARM64_EXECUTABLE}" "${GAME_ROOT}/NETfishing.aarch64" +install -m 0644 "${ARM64_PCK}" "${GAME_ROOT}/NETfishing.pck" +install -m 0644 "${TEMPLATE_ROOT}/gameinfo.xml" "${GAME_ROOT}/gameinfo.xml" +install -m 0644 "${TEMPLATE_ROOT}/screenshot.png" "${GAME_ROOT}/screenshot.png" +install -m 0644 \ + "${PROJECT_ROOT}/docs/ATTRIBUTION.md" \ + "${GAME_ROOT}/licenses/ATTRIBUTION.md" +install -m 0644 \ + "${PROJECT_ROOT}/LICENSE" \ + "${GAME_ROOT}/licenses/GPL-3.0-or-later.txt" +install -m 0644 \ + "${PROJECT_ROOT}/ASSET-LICENSE.md" \ + "${GAME_ROOT}/licenses/NETFISHING-ASSET-LICENSE.md" +install -m 0644 \ + "${PROJECT_ROOT}/TRADEMARKS.md" \ + "${GAME_ROOT}/licenses/TRADEMARKS.md" +install -m 0644 \ + "${PROJECT_ROOT}/ui/fonts/Tuffy-LICENSE.txt" \ + "${GAME_ROOT}/licenses/Tuffy-LICENSE.txt" +install -m 0644 \ + "${PROJECT_ROOT}/ui/fonts/Seattle-Avenue-LICENSE.txt" \ + "${GAME_ROOT}/licenses/Seattle-Avenue-LICENSE.txt" + +cat >"${GAME_ROOT}/BUILD-INFO.txt" <"${GAME_ROOT}/licenses/SOURCE-CODE.md" <"${GAME_ROOT}/README.md" </dev/null +readonly TOP_LEVELS="$( + unzip -Z1 "${ARCHIVE}" | cut -d/ -f1 | sort -u +)" +readonly EXPECTED_TOP_LEVELS="$(printf '%s\n' NETfishing.sh netfishing port.json)" +if [[ "${TOP_LEVELS}" != "${EXPECTED_TOP_LEVELS}" ]]; then + echo "Unexpected PortMaster archive roots:" >&2 + printf '%s\n' "${TOP_LEVELS}" >&2 + exit 1 +fi +grep -q '"version": 4' "${STAGE_ROOT}/port.json" +grep -q '"name": "netfishing.zip"' "${STAGE_ROOT}/port.json" +grep -q '^# PORTMASTER: netfishing.zip, NETfishing.sh$' \ + "${STAGE_ROOT}/NETfishing.sh" +if grep -q 'GPTOKEYB' "${STAGE_ROOT}/NETfishing.sh"; then + echo "GPTOKEYB must not be enabled for NETfishing." >&2 + exit 1 +fi +if unzip -Z1 "${ARCHIVE}" | grep -E \ + '(^|/)(conf|\.git|\.godot|logs?|saves?|identities?)(/|$)|\.(gd|tscn|blend)$' \ + >/dev/null; then + echo "The PortMaster archive contains prohibited release content." >&2 + exit 1 +fi + +sha256sum "${ARCHIVE}" +echo "PortMaster package created at ${ARCHIVE}" diff --git a/scripts/generate_placeholder_animalese.gd b/scripts/generate_placeholder_animalese.gd new file mode 100644 index 0000000..8119fd3 --- /dev/null +++ b/scripts/generate_placeholder_animalese.gd @@ -0,0 +1,71 @@ +extends SceneTree + +const OUTPUT_DIRECTORY := "res://sound/dialogue/animalese/placeholder" +const SAMPLE_RATE: int = 22050 +const SAMPLE_SECONDS: float = 0.055 +const CHARACTERS := "abcdefghijklmnopqrstuvwxyz" +const VOWELS := "aeiou" + + +func _initialize() -> void: + var absolute_directory := ProjectSettings.globalize_path(OUTPUT_DIRECTORY) + var directory_error: Error = DirAccess.make_dir_recursive_absolute( + absolute_directory + ) + if directory_error != OK: + push_error("Could not create animalese sample directory.") + quit(1) + return + for character_index: int in range(CHARACTERS.length()): + var character := CHARACTERS.substr(character_index, 1) + _save_sample(character, character_index) + _save_sample("fallback", CHARACTERS.length()) + quit() + + +func _save_sample(sample_name: String, sample_index: int) -> void: + var stream := _build_sample(sample_name, sample_index) + var output_path := "%s/%s" % [OUTPUT_DIRECTORY, sample_name] + var save_error: Error = stream.save_to_wav(output_path) + if save_error != OK: + push_error("Could not save animalese sample: %s" % sample_name) + + +func _build_sample(sample_name: String, sample_index: int) -> AudioStreamWAV: + var frame_count := roundi(SAMPLE_RATE * SAMPLE_SECONDS) + var pcm := PackedByteArray() + pcm.resize(frame_count * 2) + var base_frequency := 245.0 + float(sample_index % 7) * 18.0 + var formant_one := 620.0 + float(sample_index % 5) * 85.0 + var formant_two := 1320.0 + float(sample_index % 6) * 115.0 + var is_vowel := VOWELS.contains(sample_name) + var noise_seed := sample_index * 7919 + 104729 + for frame_index: int in range(frame_count): + var time := float(frame_index) / SAMPLE_RATE + var attack := minf(time / 0.004, 1.0) + var release := minf((SAMPLE_SECONDS - time) / 0.014, 1.0) + var envelope := maxf(minf(attack, release), 0.0) + noise_seed = int( + (noise_seed * 1103515245 + 12345) & 0x7fffffff + ) + var noise := float(noise_seed % 65536) / 32767.5 - 1.0 + var voiced := ( + sin(TAU * base_frequency * time) * 0.42 + + sin(TAU * base_frequency * 2.0 * time) * 0.16 + ) + var formants := ( + sin(TAU * formant_one * time) * 0.18 + + sin(TAU * formant_two * time) * 0.10 + ) + var consonant_noise := noise * (0.08 if is_vowel else 0.24) + var value := envelope * (voiced + formants + consonant_noise) * 0.72 + var signed_sample := clampi(roundi(value * 32767.0), -32768, 32767) + var encoded_sample := signed_sample if signed_sample >= 0 else signed_sample + 65536 + pcm[frame_index * 2] = encoded_sample & 0xff + pcm[frame_index * 2 + 1] = (encoded_sample >> 8) & 0xff + var stream := AudioStreamWAV.new() + stream.set("format", AudioStreamWAV.FORMAT_16_BITS) + stream.mix_rate = SAMPLE_RATE + stream.stereo = false + stream.data = pcm + return stream diff --git a/scripts/generate_placeholder_animalese.gd.uid b/scripts/generate_placeholder_animalese.gd.uid new file mode 100644 index 0000000..9399a64 --- /dev/null +++ b/scripts/generate_placeholder_animalese.gd.uid @@ -0,0 +1 @@ +uid://cwqm75icgcr4u diff --git a/scripts/normalize_texture_imports.gd b/scripts/normalize_texture_imports.gd new file mode 100644 index 0000000..ae36fa0 --- /dev/null +++ b/scripts/normalize_texture_imports.gd @@ -0,0 +1,204 @@ +extends SceneTree + +const DEFAULT_PROFILE: Dictionary = { + "params": { + "compress/mode": 2, + "compress/high_quality": false, + "compress/lossy_quality": 0.7, + "compress/uastc_level": 0, + "compress/rdo_quality_loss": 0.0, + "compress/hdr_compression": 1, + "compress/normal_map": 0, + "compress/channel_pack": 0, + "mipmaps/generate": false, + "mipmaps/limit": -1, + "roughness/mode": 0, + "roughness/src_normal": "", + "process/channel_remap/red": 0, + "process/channel_remap/green": 1, + "process/channel_remap/blue": 2, + "process/channel_remap/alpha": 3, + "process/fix_alpha_border": true, + "process/premult_alpha": false, + "process/normal_map_invert_y": false, + "process/hdr_as_srgb": false, + "process/hdr_clamp_exposure": false, + "process/size_limit": 0, + "detect_3d/compress_to": 0, + }, +} + +var apply_changes: bool = false +var verbose: bool = false +var roots: PackedStringArray = PackedStringArray() +var should_exit: bool = false + +var errors: int = 0 +var skipped_non_texture: int = 0 +var updated: int = 0 +var unchanged: int = 0 + +func _initialize() -> void: + _parse_args() + if should_exit: + return + if roots.is_empty(): + printerr("At least one --root path is required.") + quit(2) + return + + for root: String in roots: + var normalized_root := root + if not normalized_root.ends_with("/"): + normalized_root += "/" + if not normalized_root.begins_with("res://") and not normalized_root.is_absolute_path(): + if normalized_root.begins_with("./"): + normalized_root = "res://%s" % normalized_root.trim_prefix("./") + else: + normalized_root = "res://%s" % normalized_root + _print("Scanning %s" % normalized_root) + _scan_directory(normalized_root) + + print_summary() + quit(1 if errors > 0 else 0) + + +func _parse_args() -> void: + var args := OS.get_cmdline_user_args() + var i := 0 + while i < args.size(): + var arg := args[i] + if arg == "--help" or arg == "-h": + _print_usage() + should_exit = true + quit(0) + return + elif arg == "--apply": + apply_changes = true + i += 1 + elif arg == "--verbose" or arg == "-v": + verbose = true + i += 1 + elif arg == "--root": + if i + 1 >= args.size(): + printerr("--root requires a path value") + should_exit = true + quit(1) + return + i += 1 + roots.append(args[i]) + i += 1 + elif arg.begins_with("--root="): + roots.append(arg.trim_prefix("--root=")) + i += 1 + else: + printerr("Unknown argument: ", arg) + should_exit = true + quit(2) + return + + +func _scan_directory(path: String) -> void: + var dir := DirAccess.open(path) + if dir == null: + printerr("Failed to open root: ", path) + errors += 1 + return + + dir.list_dir_begin() + while true: + var name := dir.get_next() + if name.is_empty(): + break + if name == "." or name == "..": + continue + if name == ".godot": + continue + + var candidate := path.path_join(name) + if dir.current_is_dir(): + _scan_directory(candidate) + continue + + if name.get_extension().to_lower() != "import": + continue + + _handle_import_file(candidate) + + +func _handle_import_file(import_path: String) -> void: + var cfg := ConfigFile.new() + var err := cfg.load(import_path) + if err != OK: + printerr("Could not load import settings: ", import_path, " (", err, ")") + errors += 1 + return + + if not cfg.has_section("remap") or not cfg.has_section_key("remap", "importer"): + skipped_non_texture += 1 + return + + if str(cfg.get_value("remap", "importer")) != "texture": + skipped_non_texture += 1 + return + + var changed := false + for section_name: String in DEFAULT_PROFILE.keys(): + var section_data: Dictionary = DEFAULT_PROFILE[section_name] + for section_key: String in section_data.keys(): + var desired: Variant = section_data[section_key] + var existing: Variant = null + if cfg.has_section(section_name) and cfg.has_section_key(section_name, section_key): + existing = cfg.get_value(section_name, section_key) + if existing != desired: + cfg.set_value(section_name, section_key, desired) + changed = true + + if not changed: + unchanged += 1 + if verbose: + _print("Unchanged: %s" % import_path) + return + + if apply_changes: + err = cfg.save(import_path) + if err != OK: + printerr("Could not save import settings: ", import_path, " (", err, ")") + errors += 1 + return + if verbose: + _print("Updated: %s" % import_path) + else: + _print("Would update: %s" % import_path) + + updated += 1 + + +func print_summary() -> void: + if apply_changes: + _print( + "Texture import normalization complete: " + + "updated=%d unchanged=%d skipped_non_texture=%d errors=%d" + % [updated, unchanged, skipped_non_texture, errors] + ) + else: + _print( + "Dry-run complete: " + + "would-update=%d unchanged=%d skipped_non_texture=%d errors=%d" + % [updated, unchanged, skipped_non_texture, errors] + ) + _print("Pass --apply to write normalized settings into .import files.") + + +func _print(msg: String) -> void: + print(msg) + + +func _print_usage() -> void: + print("Usage:") + print(" godot --headless --path . --script scripts/normalize_texture_imports.gd [--apply] [--verbose] [--root ] [--root=] [--help]") + print() + print("Defaults:") + print("- Dry-run only unless --apply is provided") + print("- Requires at least one explicit --root path") + print("- Skips .godot") diff --git a/scripts/normalize_texture_imports.gd.uid b/scripts/normalize_texture_imports.gd.uid new file mode 100644 index 0000000..d2006e5 --- /dev/null +++ b/scripts/normalize_texture_imports.gd.uid @@ -0,0 +1 @@ +uid://b24mlapvqxj5a diff --git a/scripts/normalize_texture_profiles.sh b/scripts/normalize_texture_profiles.sh new file mode 100755 index 0000000..1483a61 --- /dev/null +++ b/scripts/normalize_texture_profiles.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + +readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" +readonly GODOT_BIN="${GODOT_BIN:-godot}" + +if [[ $# -ne 1 ]]; then + echo "usage: scripts/normalize_texture_profiles.sh res://path/to/textures" >&2 + exit 2 +fi + +readonly ROOT_PATH="$1" + +readonly WORK_ROOT="$(mktemp -d -t netfishing-godot-data.XXXXXX)" +trap 'rm -rf -- "${WORK_ROOT}"' EXIT + +cd "${PROJECT_ROOT}" + +export XDG_DATA_HOME="${WORK_ROOT}/data" +export XDG_CONFIG_HOME="${WORK_ROOT}/config" +export XDG_CACHE_HOME="${WORK_ROOT}/cache" + +"${GODOT_BIN}" --headless --path . \ + --script scripts/normalize_texture_imports.gd -- --apply --root "${ROOT_PATH}" + +# Synchronize Godot-generated remap and destination metadata with the updated +# import parameters before the caller reviews or commits the sidecars. +"${GODOT_BIN}" --headless --path . --import diff --git a/scripts/portmaster/NETfishing.sh b/scripts/portmaster/NETfishing.sh new file mode 100755 index 0000000..58fbfbd --- /dev/null +++ b/scripts/portmaster/NETfishing.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# PORTMASTER: netfishing.zip, NETfishing.sh + +XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} + +if [ -d "/opt/system/Tools/PortMaster/" ]; then + controlfolder="/opt/system/Tools/PortMaster" +elif [ -d "/opt/tools/PortMaster/" ]; then + controlfolder="/opt/tools/PortMaster" +elif [ -d "$XDG_DATA_HOME/PortMaster/" ]; then + controlfolder="$XDG_DATA_HOME/PortMaster" +else + controlfolder="/roms/ports/PortMaster" +fi + +if [ ! -f "$controlfolder/control.txt" ]; then + echo "PortMaster control.txt was not found at $controlfolder" >&2 + exit 1 +fi + +source "$controlfolder/control.txt" +[ -f "${controlfolder}/mod_${CFW_NAME}.txt" ] && source "${controlfolder}/mod_${CFW_NAME}.txt" +get_controls + +GAMEDIR="/${directory}/ports/netfishing" +if [ ! -d "$GAMEDIR" ] && [ -d "/mnt/mmc/ports/netfishing" ]; then + GAMEDIR="/mnt/mmc/ports/netfishing" +fi + +CONFDIR="$GAMEDIR/conf" +GAME_EXECUTABLE="$GAMEDIR/NETfishing.aarch64" +WESTON_DIR="/tmp/netfishing-weston" +WESTON_RUNTIME="weston_pkg_0.2" +HARBOURMASTER="$controlfolder/harbourmaster" +if [ ! -x "$HARBOURMASTER" ] && [ -x "/mnt/mmc/MUOS/PortMaster/harbourmaster" ]; then + HARBOURMASTER="/mnt/mmc/MUOS/PortMaster/harbourmaster" +fi + +mkdir -p "$CONFDIR/data" "$CONFDIR/config" "$CONFDIR/cache" "$WESTON_DIR" +chmod +x "$GAME_EXECUTABLE" + +> "$GAMEDIR/log.txt" && exec > >(tee "$GAMEDIR/log.txt") 2>&1 + +if [ ! -f "$controlfolder/libs/${WESTON_RUNTIME}.squashfs" ]; then + if [ ! -x "$HARBOURMASTER" ]; then + pm_message "NETfishing requires the latest PortMaster and WestonPack runtime." + sleep 5 + exit 1 + fi + $ESUDO "$HARBOURMASTER" --quiet --no-check runtime_check "${WESTON_RUNTIME}.squashfs" +fi + +if [ ! -f "$controlfolder/libs/${WESTON_RUNTIME}.squashfs" ]; then + pm_message "WestonPack could not be installed. Check the network connection and PortMaster runtime manager." + sleep 5 + exit 1 +fi + +if [[ "$PM_CAN_MOUNT" != "N" ]]; then + $ESUDO umount "$WESTON_DIR" 2>/dev/null +fi +$ESUDO mount "$controlfolder/libs/${WESTON_RUNTIME}.squashfs" "$WESTON_DIR" + +cd "$GAMEDIR" || exit 1 + +# Godot 4.7 identifies the muOS H700 virtual controller with a CRC-bearing +# GUID and numbers its buttons without the volume keys exposed by SDL2. The +# PortMaster SDL2 database entry therefore cannot drive Godot's gamepad layer. +GODOT_MUOS_MAPPING="19004ca6010000000100000000010000,muOS-Keys,a:b0,b:b1,x:b3,y:b2,leftshoulder:b4,rightshoulder:b5,lefttrigger:b10,righttrigger:b11,guide:b8,start:b7,back:b6,dpup:h0.1,dpleft:h0.8,dpright:h0.2,dpdown:h0.4,leftx:a0,lefty:a1,leftstick:b9,rightx:a2,righty:a3,rightstick:b12,platform:Linux," +MUOS_SDL2_GUID="19000000010000000100000000010000" +if [[ "$sdl_controllerconfig" == "${MUOS_SDL2_GUID},"* ]]; then + netfishing_controllerconfig="$GODOT_MUOS_MAPPING" +elif [ -n "$sdl_controllerconfig" ]; then + netfishing_controllerconfig="$sdl_controllerconfig" +else + netfishing_controllerconfig="$GODOT_MUOS_MAPPING" +fi + +NETFISHING_GODOT_OPTIONS=() +NETFISHING_GAME_ENVIRONMENT=() +DEVICE_COMPATIBILITY="" +if [[ -r /proc/device-tree/compatible ]]; then + DEVICE_COMPATIBILITY="$(tr '\0' ' ' /dev/null +fi +pm_finish diff --git a/scripts/portmaster/gameinfo.xml b/scripts/portmaster/gameinfo.xml new file mode 100755 index 0000000..ef608bd --- /dev/null +++ b/scripts/portmaster/gameinfo.xml @@ -0,0 +1,13 @@ + + + + ./NETfishing.sh + NETfishing + A relaxed multiplayer fishing game about exploring a small island, catching and collecting fish, customizing your character, and spending time by the water. + 20260804T000000 + NETfishing project + Independent + adventure, simulation + ./netfishing/screenshot.png + + diff --git a/scripts/portmaster/port.json b/scripts/portmaster/port.json new file mode 100644 index 0000000..98df160 --- /dev/null +++ b/scripts/portmaster/port.json @@ -0,0 +1,38 @@ +{ + "version": 4, + "name": "netfishing.zip", + "items": [ + "NETfishing.sh", + "netfishing/" + ], + "items_opt": null, + "attr": { + "title": "NETfishing", + "porter": [ + "Voyager" + ], + "desc": "A relaxed multiplayer fishing game about exploring a small island, catching and collecting fish, customizing your character, and spending time by the water.", + "desc_md": null, + "inst": "Ready to run on supported ARM64 PortMaster devices. This alpha package requires two analog sticks.", + "inst_md": null, + "genres": [ + "adventure", + "simulation" + ], + "image": {}, + "rtr": false, + "exp": false, + "runtime": [ + "weston_pkg_0.2.squashfs" + ], + "store": [], + "availability": "full", + "reqs": [ + "analog_2" + ], + "arch": [ + "aarch64" + ], + "min_glibc": "2.28" + } +} diff --git a/scripts/portmaster/screenshot.png b/scripts/portmaster/screenshot.png new file mode 100755 index 0000000..4fe8f2e Binary files /dev/null and b/scripts/portmaster/screenshot.png differ diff --git a/scripts/portmaster/screenshot.png.import b/scripts/portmaster/screenshot.png.import new file mode 100644 index 0000000..3aa5f2d --- /dev/null +++ b/scripts/portmaster/screenshot.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ww0w8iubiwsf" +path="res://.godot/imported/screenshot.png-6eed5589b80ced9ab24aed11cadf34f5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scripts/portmaster/screenshot.png" +dest_files=["res://.godot/imported/screenshot.png-6eed5589b80ced9ab24aed11cadf34f5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scripts/run_validations.sh b/scripts/run_validations.sh new file mode 100755 index 0000000..7ae8929 --- /dev/null +++ b/scripts/run_validations.sh @@ -0,0 +1,235 @@ +#!/usr/bin/env bash + +set -euo pipefail + +readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" +readonly GODOT_BIN="${GODOT_BIN:-godot}" +readonly TEST_TIMEOUT_SECONDS="${TEST_TIMEOUT_SECONDS:-120}" +readonly RUN_ROOT="$(mktemp -d -t netfishing-validations.XXXXXX)" + +readonly -a QUICK_TESTS=( + "tests/android_readiness_validation.gd" + "tests/dedicated_server_config_validation.gd" + "tests/fish_catalog_content_validation.gd" + "tests/fish_quality_validation.gd" + "tests/fishing_audio_validation.gd" + "tests/fishing_surface_validation.gd" + "tests/fur_pattern_validation.gd" + "tests/logbook_validation.gd" + "tests/network_player_animation_protocol_validation.gd" + "tests/player_experience_validation.gd" + "tests/shoreline_ambience_validation.gd" + "tests/surface_drawing_validation.gd" + "tests/terrain_blender_material_validation.gd" + "tests/texture_sampling_validation.gd" + "tests/world_time_validation.gd" + "tests/world_weather_validation.gd" +) + +readonly -a RUNTIME_TESTS=( + "tests/inventory_notepad_art_validation.gd" + "tests/logbook_runtime_validation.gd" + "tests/player_experience_ui_validation.gd" + "tests/title_credits_validation.gd" + "tests/ui_scaling_runtime_validation.gd" +) + +readonly -a HOST_TESTS=( + "tests/art_tools_validation.gd" + "tests/dedicated_host_session_validation.gd" + "tests/economy_regression_validation.gd" + "tests/fish_hotbar_showcase_validation.gd" + "tests/fishing_authority_validation.gd" + "tests/job_system_validation.gd" + "tests/surface_drawing_runtime_validation.gd" +) + +readonly -a NETWORK_TESTS=( + "tests/economy_regression_validation.gd" + "tests/fish_showcase_multiplayer_validation.gd" + "tests/fishing_multiplayer_validation.gd" + "tests/job_multiplayer_validation.gd" + "tests/movement_multiplayer_validation.gd" + "tests/operator_multiplayer_validation.gd" + "tests/surface_drawing_multiplayer_validation.gd" + "tests/world_time_multiplayer_validation.gd" +) + +cleanup() { + rm -rf -- "${RUN_ROOT}" +} +trap cleanup EXIT INT TERM + +usage() { + printf 'Usage: %s {quick|full|host|network|all|--list}\n' "$0" +} + +prepare_root() { + local name="$1" + local root="${RUN_ROOT}/${name}" + mkdir -p -- "${root}/data" "${root}/config" "${root}/cache" + printf '%s\n' "${root}" +} + +run_test() { + local script="$1" + local name="${script#tests/}" + name="${name%.gd}" + local root + root="$(prepare_root "${name}")" + printf '\n==> %s\n' "${script}" + XDG_DATA_HOME="${root}/data" \ + XDG_CONFIG_HOME="${root}/config" \ + XDG_CACHE_HOME="${root}/cache" \ + timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \ + --headless --path "${PROJECT_ROOT}" --script "${script}" +} + + +wait_for_network_host() { + local script="$1" + local host_pid="$2" + local test_port="7777" + local declared_port + declared_port="$( + sed -n -E \ + 's/^const TEST_PORT(: int)? = ([0-9]+)$/\2/p' \ + "${PROJECT_ROOT}/${script}" + )" + if [[ -n "${declared_port}" ]]; then + test_port="${declared_port}" + fi + if ! command -v ss >/dev/null 2>&1; then + sleep 2 + return 0 + fi + for _attempt in {1..100}; do + if ! kill -0 "${host_pid}" 2>/dev/null; then + return 1 + fi + if ss -H -lun "sport = :${test_port}" | grep -q .; then + return 0 + fi + sleep 0.1 + done + return 1 +} + + +run_network_test() { + local script="$1" + local name="${script#tests/}" + name="${name%.gd}" + local host_root client_root host_pid host_status client_status + host_root="$(prepare_root "${name}-host")" + client_root="$(prepare_root "${name}-client")" + printf '\n==> %s (host + client)\n' "${script}" + XDG_DATA_HOME="${host_root}/data" \ + XDG_CONFIG_HOME="${host_root}/config" \ + XDG_CACHE_HOME="${host_root}/cache" \ + timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \ + --headless --path "${PROJECT_ROOT}" --script "${script}" -- host \ + >"${host_root}/output.log" 2>&1 & + host_pid=$! + if ! wait_for_network_host "${script}" "${host_pid}"; then + set +e + wait "${host_pid}" + host_status=$? + set -e + printf '%s\n' '-- host output --' + sed -n '1,240p' "${host_root}/output.log" + printf 'error: host did not become ready (exit %d)\n' \ + "${host_status}" >&2 + return 1 + fi + set +e + XDG_DATA_HOME="${client_root}/data" \ + XDG_CONFIG_HOME="${client_root}/config" \ + XDG_CACHE_HOME="${client_root}/cache" \ + timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \ + --headless --path "${PROJECT_ROOT}" --script "${script}" -- client \ + >"${client_root}/output.log" 2>&1 + client_status=$? + wait "${host_pid}" + host_status=$? + set -e + printf '%s\n' '-- host output --' + sed -n '1,240p' "${host_root}/output.log" + printf '%s\n' '-- client output --' + sed -n '1,240p' "${client_root}/output.log" + if ((host_status != 0 || client_status != 0)); then + printf 'error: host exited %d; client exited %d\n' \ + "${host_status}" "${client_status}" >&2 + return 1 + fi +} + +run_quick() { + local test_script + for test_script in "${QUICK_TESTS[@]}"; do + run_test "${test_script}" + done +} + +run_full() { + local test_script + run_quick + for test_script in "${RUNTIME_TESTS[@]}"; do + run_test "${test_script}" + done +} + +run_host() { + local test_script + for test_script in "${HOST_TESTS[@]}"; do + run_test "${test_script}" + done +} + +run_network() { + local test_script + for test_script in "${NETWORK_TESTS[@]}"; do + run_network_test "${test_script}" + done +} + +list_tests() { + printf '%s\n' 'Quick tests:' + printf ' %s\n' "${QUICK_TESTS[@]}" + printf '%s\n' 'Additional full-suite tests:' + printf ' %s\n' "${RUNTIME_TESTS[@]}" + printf '%s\n' 'Single-process tests requiring a local UDP bind:' + printf ' %s\n' "${HOST_TESTS[@]}" + printf '%s\n' 'Paired network tests:' + printf ' %s\n' "${NETWORK_TESTS[@]}" +} + +case "${1:-}" in + quick) + run_quick + ;; + full) + run_full + ;; + host) + run_host + ;; + network) + run_network + ;; + all) + run_full + run_host + run_network + ;; + --list) + list_tests + ;; + *) + usage >&2 + exit 2 + ;; +esac + +printf '\nAll requested validations passed.\n' diff --git a/scripts/setup_android_build.sh b/scripts/setup_android_build.sh new file mode 100755 index 0000000..c1cebdc --- /dev/null +++ b/scripts/setup_android_build.sh @@ -0,0 +1,394 @@ +#!/usr/bin/env bash +set -euo pipefail + +ANDROID_HOME="${ANDROID_HOME:-${HOME}/Android/Sdk}" +JAVA_HOME="${JAVA_HOME:-}" +INSTALL=0 +AUTO=0 +CMDLINE_TOOLS_ZIP="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip" +SDK_VERSION="33" +BUILDTOOLS_VERSION="34.0.0" + +usage() { + cat <&2 + usage + exit 2 ;; + esac +done + +PROJECT_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +GODOT="${GODOT_BIN:-godot}" +PROJECT_VERSION="$( + sed -n 's/^config\/version="\([^"]*\)"/\1/p' "${PROJECT_ROOT}/project.godot" +)" +MISSING=0 + +print_status() { printf '%s %s\n' "[$1]" "$2"; } + +check_dir() { + local path="$1" label="$2" + if [[ -d "$path" ]]; then + print_status OK "${label}: ${path}" + else + print_status ERR "Missing ${label}: ${path}" + MISSING=$((MISSING+1)) + fi +} + +check_file() { + local path="$1" label="$2" + if [[ -x "$path" ]]; then + print_status OK "${label}: ${path}" + else + print_status ERR "Missing ${label}: ${path}" + MISSING=$((MISSING+1)) + fi +} + +resolve_java_home() { + local candidate="" + if [[ -n "$JAVA_HOME" && -x "$JAVA_HOME/bin/java" ]]; then + return 0 + fi + + if command -v java >/dev/null 2>&1; then + candidate="$(readlink -f "$(command -v java)")" + candidate="$(dirname "$(dirname "$candidate")")" + if [[ -x "$candidate/bin/java" ]]; then + JAVA_HOME="$candidate" + return 0 + fi + fi + + # Common Ubuntu/Debian/JDK locations + for p in \ + /usr/lib/jvm/default-java \ + /usr/lib/jvm/java-17-openjdk-amd64 \ + /usr/lib/jvm/java-17-openjdk \ + /usr/lib/jvm/jdk-17* \ + /usr/lib/jvm/temurin-17*; + do + if [[ -e $p && -x "$p/bin/java" ]]; then + JAVA_HOME="$p" + return 0 + fi + done + return 1 +} + +check_java() { + if resolve_java_home && [[ -x "$JAVA_HOME/bin/java" ]]; then + print_status OK "Java binary: $JAVA_HOME/bin/java" + check_file "$JAVA_HOME/bin/javac" "javac binary" + check_file "$JAVA_HOME/bin/keytool" "keytool binary" + "$JAVA_HOME/bin/java" -version 2>&1 | sed -n '1,2p' + local java_ver + java_ver=$("$JAVA_HOME/bin/java" -version 2>&1 | sed -n '1p') + if [[ "$java_ver" =~ \"([0-9]+)\\. ]]; then + local java_major="${BASH_REMATCH[1]}" + if [[ "$java_major" != "17" ]]; then + print_status WARN "Detected Java $java_major. Godot Android export is commonly validated with JDK 17." + fi + fi + else + print_status ERR "Java not found. Set JAVA_HOME to a JDK 17+ home or install one." + MISSING=$((MISSING+1)) + JAVA_HOME="" + fi +} + +find_sdkmanager() { + if [[ -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]]; then + echo "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" + return + fi + if [[ -x "$ANDROID_HOME/cmdline-tools/bin/sdkmanager" ]]; then + echo "$ANDROID_HOME/cmdline-tools/bin/sdkmanager"; return + fi + if [[ -x "$ANDROID_HOME/tools/bin/sdkmanager" ]]; then + echo "$ANDROID_HOME/tools/bin/sdkmanager"; return + fi + if command -v sdkmanager >/dev/null 2>&1; then + command -v sdkmanager + return + fi + echo "" +} + +sdkmanager_path="$(find_sdkmanager)" +TMP_CMDLINE_TOOLS_DIR="" + +cleanup_tmpdir() { + [[ -n "${TMP_CMDLINE_TOOLS_DIR:-}" && -d "$TMP_CMDLINE_TOOLS_DIR" ]] && rm -rf "$TMP_CMDLINE_TOOLS_DIR" + TMP_CMDLINE_TOOLS_DIR="" +} + +check_sdk_paths() { + print_status INFO "ANDROID_HOME: ${ANDROID_HOME}" + check_dir "$ANDROID_HOME" "Android SDK root" + check_dir "$ANDROID_HOME/platform-tools" "platform-tools" + check_file "$ANDROID_HOME/platform-tools/adb" "adb binary" + + if [[ -d "$ANDROID_HOME/build-tools/$BUILDTOOLS_VERSION" ]]; then + print_status OK "build-tools ${BUILDTOOLS_VERSION}: present" + check_file \ + "$ANDROID_HOME/build-tools/$BUILDTOOLS_VERSION/apksigner" \ + "apksigner" + else + print_status ERR "Missing build-tools version: ${BUILDTOOLS_VERSION}" + MISSING=$((MISSING+1)) + fi + + if [[ -d "$ANDROID_HOME/platforms" ]]; then + if ls "$ANDROID_HOME/platforms/android-${SDK_VERSION}" >/dev/null 2>&1; then + print_status OK "platform ${SDK_VERSION}: present" + else + print_status ERR "Missing Android platform: android-${SDK_VERSION}" + MISSING=$((MISSING+1)) + fi + else + print_status ERR "Missing platforms directory: $ANDROID_HOME/platforms" + MISSING=$((MISSING+1)) + fi + + if [[ -x "$sdkmanager_path" ]]; then + print_status OK "sdkmanager: ${sdkmanager_path}" + else + print_status ERR "sdkmanager not found in SDK or PATH" + MISSING=$((MISSING+1)) + fi +} + +install_jdk_hint() { + local pm=0 + local -a elevate=() + echo "Attempting automatic JDK install..." + if command -v apt-get >/dev/null 2>&1; then + pm="apt-get" + elif command -v dnf >/dev/null 2>&1; then + pm="dnf" + elif command -v pacman >/dev/null 2>&1; then + pm="pacman" + else + echo "No supported package manager found (apt-get/dnf/pacman)." + echo "Install JDK 17 manually, then rerun with --java-home set." + return 1 + fi + + if [[ $EUID -ne 0 ]]; then + if ! command -v sudo >/dev/null 2>&1; then + echo "sudo not found; please run with root privileges for package install." + return 1 + fi + elevate=(sudo) + fi + + if [[ "$pm" == "apt-get" ]]; then + echo "apt-get install -y openjdk-17-jdk" + if [[ $AUTO -eq 1 ]]; then + "${elevate[@]}" apt-get update + "${elevate[@]}" apt-get install -y openjdk-17-jdk + else + echo "Run as manual step first: sudo apt-get update && sudo apt-get install -y openjdk-17-jdk" + return 1 + fi + elif [[ "$pm" == "dnf" ]]; then + echo "dnf install -y java-17-openjdk-devel" + if [[ $AUTO -eq 1 ]]; then + "${elevate[@]}" dnf install -y java-17-openjdk-devel + else + return 1 + fi + else + echo "pacman -S --noconfirm jdk17-openjdk" + if [[ $AUTO -eq 1 ]]; then + "${elevate[@]}" pacman -S --noconfirm jdk17-openjdk + else + return 1 + fi + fi +} + +install_cmdline_tools() { + if [[ -x "$sdkmanager_path" ]]; then + return 0 + fi + + echo "Downloading Android cmdline-tools to bootstrap sdkmanager..." + TMP_CMDLINE_TOOLS_DIR="" + TMP_CMDLINE_TOOLS_DIR="$(mktemp -d)" + if [[ -z "$TMP_CMDLINE_TOOLS_DIR" ]]; then + echo "Failed to create temporary directory." + return 1 + fi + trap cleanup_tmpdir RETURN + + if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then + echo "curl/wget required to download cmdline-tools. Install one and rerun." + return 1 + fi + + local zipfile="${TMP_CMDLINE_TOOLS_DIR}/cmdline-tools.zip" + if command -v curl >/dev/null 2>&1; then + if ! curl -fL "$CMDLINE_TOOLS_ZIP" -o "$zipfile"; then + echo "Failed to download cmdline-tools from $CMDLINE_TOOLS_ZIP" + return 1 + fi + else + if ! wget -q "$CMDLINE_TOOLS_ZIP" -O "$zipfile"; then + echo "Failed to download cmdline-tools from $CMDLINE_TOOLS_ZIP" + return 1 + fi + fi + + if [[ ! -s "$zipfile" ]]; then + echo "Downloaded cmdline-tools archive is empty." + return 1 + fi + unzip -q "$zipfile" -d "$TMP_CMDLINE_TOOLS_DIR" + if [[ ! -d "$TMP_CMDLINE_TOOLS_DIR/cmdline-tools" ]]; then + echo "Downloaded archive did not contain expected cmdline-tools payload." + return 1 + fi + mkdir -p "$ANDROID_HOME" || { + echo "Cannot create Android SDK root: $ANDROID_HOME" + return 1 + } + mkdir -p "$ANDROID_HOME/cmdline-tools" + rm -rf "$ANDROID_HOME/cmdline-tools/latest" + mkdir -p "$ANDROID_HOME/cmdline-tools/latest" + mv "$TMP_CMDLINE_TOOLS_DIR/cmdline-tools"/* "$ANDROID_HOME/cmdline-tools/latest/" + echo "Installed SDK cmdline-tools to $ANDROID_HOME/cmdline-tools/latest" + sdkmanager_path="$(find_sdkmanager)" + return 0 +} + +install_sdk_components() { + if [[ -z "$sdkmanager_path" ]]; then + echo "Cannot install SDK packages without sdkmanager." + return 1 + fi + "$sdkmanager_path" --sdk_root="${ANDROID_HOME}" --list | sed -n '1,40p' + echo "Installing required SDK components: platform-tools, platform-android-${SDK_VERSION}, build-tools-${BUILDTOOLS_VERSION}" + if [[ $AUTO -eq 1 ]]; then + yes | "$sdkmanager_path" --sdk_root="${ANDROID_HOME}" --install "platform-tools" "platforms;android-${SDK_VERSION}" "build-tools;${BUILDTOOLS_VERSION}" + else + echo "Run this manually after accepting licenses:" + echo " yes | \"${sdkmanager_path}\" --sdk_root=\"${ANDROID_HOME}\" --install \"platform-tools\" \"platforms;android-${SDK_VERSION}\" \"build-tools;${BUILDTOOLS_VERSION}\"" + return 1 + fi +} + +check_editor_settings() { + local editor_settings="$HOME/.config/godot/editor_settings-4.7.tres" + print_status INFO "Checking editor settings: $editor_settings" + if [[ -f "$editor_settings" ]]; then + if rg -n 'export/android/android_sdk_path|export/android/java_sdk_path' "$editor_settings" >/dev/null 2>&1; then + rg -n 'export/android/android_sdk_path|export/android/java_sdk_path' "$editor_settings" + else + print_status WARN "Editor export settings entries are missing for Android in this file" + echo " Set in editor: Export > Presets > Android:" + echo " android_sdk_path = ${ANDROID_HOME}" + echo " java_sdk_path = ${JAVA_HOME}" + fi + else + print_status WARN "No editor settings file found at expected path: $editor_settings" + fi +} + +run_export_hint() { + echo + echo "Example export commands:" + echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-release \"Android\" builds/${PROJECT_VERSION}/android/NETfishing.apk" + echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-debug \"Android\" builds/android/NETfishing-debug.apk" +} + +print_status INFO "Starting Android dependency preflight..." +check_java +check_sdk_paths +check_editor_settings + +if [[ $MISSING -eq 0 ]]; then + print_status OK "Dependency check passed." + run_export_hint + exit 0 +fi + +if [[ $INSTALL -eq 1 ]]; then + echo + print_status INFO "Attempting dependency install path..." + if [[ \ + -z "$JAVA_HOME" \ + || ! -x "$JAVA_HOME/bin/java" \ + || ! -x "$JAVA_HOME/bin/javac" \ + || ! -x "$JAVA_HOME/bin/keytool" \ + ]]; then + install_jdk_hint || true + fi + + resolve_java_home || true + check_java + + install_cmdline_tools || true + sdkmanager_path="$(find_sdkmanager)" + install_sdk_components || true + + MISSING=0 + check_java + check_sdk_paths + if [[ $MISSING -eq 0 ]]; then + echo + print_status OK "Dependencies fixed." + run_export_hint + exit 0 + fi +fi + +print_status ERR "Dependency check failed with ${MISSING} issue(s)." +cat < DedicatedServerConfig: + var result := DedicatedServerConfig.new() + result.data_directory = ProjectSettings.globalize_path( + "user://dedicated-server-data" + ) + result.discovery_url = str(ProjectSettings.get_setting( + "network/discovery/base_url", "" + )).strip_edges() + var config_path: String = OS.get_environment( + "NETFISHING_SERVER_CONFIG" + ).strip_edges() + for argument: String in OS.get_cmdline_user_args(): + if argument.begins_with("--config="): + config_path = argument.trim_prefix("--config=").strip_edges() + if not config_path.is_empty() and not result._load_file(config_path): + return result + result._apply_environment() + result._apply_arguments(OS.get_cmdline_user_args()) + result._validate() + return result + + +func is_valid() -> bool: + return error_message.is_empty() + + +func _load_file(path: String) -> bool: + var file := ConfigFile.new() + var error: Error = file.load(path) + if error != OK: + error_message = "Could not load server configuration: %s" % path + return false + server_name = str(file.get_value("server", "name", server_name)) + bind_address = str(file.get_value( + "server", "bind_address", bind_address + )) + port = int(file.get_value("server", "port", port)) + max_players = int(file.get_value( + "server", "max_players", max_players + )) + public_listing = bool(file.get_value( + "server", "public", public_listing + )) + data_directory = str(file.get_value( + "server", "data_directory", data_directory + )) + discovery_url = str(file.get_value( + "discovery", "url", discovery_url + )) + operator_fingerprints = _parse_fingerprint_list(file.get_value( + "moderation", "operators", operator_fingerprints + )) + return true + + +func _apply_environment() -> void: + server_name = _environment_string( + "NETFISHING_SERVER_NAME", server_name + ) + bind_address = _environment_string( + "NETFISHING_SERVER_BIND", bind_address + ) + port = _environment_int("NETFISHING_SERVER_PORT", port) + max_players = _environment_int( + "NETFISHING_SERVER_MAX_PLAYERS", max_players + ) + public_listing = _environment_bool( + "NETFISHING_SERVER_PUBLIC", public_listing + ) + discovery_url = _environment_string( + "NETFISHING_DISCOVERY_URL", discovery_url + ) + data_directory = _environment_string( + "NETFISHING_DATA_DIR", data_directory + ) + if OS.has_environment("NETFISHING_SERVER_OPERATORS"): + operator_fingerprints = _parse_fingerprint_list( + OS.get_environment("NETFISHING_SERVER_OPERATORS") + ) + + +func _apply_arguments(arguments: PackedStringArray) -> void: + for argument: String in arguments: + if argument.begins_with("--name="): + server_name = argument.trim_prefix("--name=") + elif argument.begins_with("--bind="): + bind_address = argument.trim_prefix("--bind=") + elif argument.begins_with("--port="): + port = _parse_int(argument.trim_prefix("--port="), port) + elif argument.begins_with("--max-players="): + max_players = _parse_int( + argument.trim_prefix("--max-players="), max_players + ) + elif argument.begins_with("--data-dir="): + data_directory = argument.trim_prefix("--data-dir=") + elif argument.begins_with("--discovery-url="): + discovery_url = argument.trim_prefix("--discovery-url=") + elif argument.begins_with("--operators="): + operator_fingerprints = _parse_fingerprint_list( + argument.trim_prefix("--operators=") + ) + elif argument == "--public": + public_listing = true + elif argument == "--private": + public_listing = false + + +func _validate() -> void: + server_name = server_name.strip_edges().left(48) + bind_address = bind_address.strip_edges() + discovery_url = discovery_url.strip_edges().trim_suffix("/") + data_directory = data_directory.strip_edges() + operator_fingerprints = _normalized_fingerprints(operator_fingerprints) + if server_name.is_empty(): + error_message = "Server name cannot be empty." + elif not _safe_text(server_name): + error_message = "Server name contains unsupported characters." + elif bind_address.is_empty(): + error_message = "Bind address cannot be empty." + elif port < 1 or port > 65535: + error_message = "Server port must be between 1 and 65535." + elif max_players < 1 or max_players > 128: + error_message = "Maximum players must be between 1 and 128." + elif not data_directory.is_empty() and not data_directory.is_absolute_path(): + error_message = "Server data directory must be an absolute path." + elif public_listing and not ( + discovery_url.begins_with("https://") + or discovery_url.begins_with("http://") + ): + error_message = "A public server requires a discovery URL." + elif operator_fingerprints.size() > MAX_OPERATORS: + error_message = "A server may configure at most %d operators." % ( + MAX_OPERATORS + ) + else: + for fingerprint: String in operator_fingerprints: + if not NetworkIdentityCrypto.valid_fingerprint(fingerprint): + error_message = ( + "Server operator fingerprints must be 64 lowercase hex characters." + ) + break + + +static func _safe_text(value: String) -> bool: + for index: int in value.length(): + var codepoint: int = value.unicode_at(index) + if codepoint < 32 or codepoint == 127: + return false + return true + + +static func _environment_string(name: String, fallback: String) -> String: + return OS.get_environment(name) if OS.has_environment(name) else fallback + + +static func _environment_int(name: String, fallback: int) -> int: + return ( + _parse_int(OS.get_environment(name), fallback) + if OS.has_environment(name) else fallback + ) + + +static func _environment_bool(name: String, fallback: bool) -> bool: + if not OS.has_environment(name): + return fallback + var value: String = OS.get_environment(name).strip_edges().to_lower() + return value in ["1", "true", "yes", "on"] + + +static func _parse_int(value: String, fallback: int) -> int: + return int(value) if value.strip_edges().is_valid_int() else fallback + + +static func _parse_fingerprint_list(value: Variant) -> PackedStringArray: + var values: PackedStringArray = PackedStringArray() + if typeof(value) == TYPE_STRING: + var text: String = str(value) + for separator: String in [";", "\n", "\r", "\t", " "]: + text = text.replace(separator, ",") + values = text.split(",", false) + elif typeof(value) in [TYPE_ARRAY, TYPE_PACKED_STRING_ARRAY]: + for entry: Variant in value: + if typeof(entry) in [TYPE_STRING, TYPE_STRING_NAME]: + values.append(str(entry)) + return _normalized_fingerprints(values) + + +static func _normalized_fingerprints( + values: PackedStringArray, +) -> PackedStringArray: + var result: PackedStringArray = PackedStringArray() + for value: String in values: + var fingerprint: String = value.strip_edges().to_lower() + if not fingerprint.is_empty() and fingerprint not in result: + result.append(fingerprint) + result.sort() + return result diff --git a/server/dedicated_server_config.gd.uid b/server/dedicated_server_config.gd.uid new file mode 100644 index 0000000..720cccc --- /dev/null +++ b/server/dedicated_server_config.gd.uid @@ -0,0 +1 @@ +uid://nmg477qkcbhe diff --git a/settings/controller_mapping_manager.gd b/settings/controller_mapping_manager.gd new file mode 100644 index 0000000..1690963 --- /dev/null +++ b/settings/controller_mapping_manager.gd @@ -0,0 +1,986 @@ +class_name ControllerMappingManager +extends Node + +signal active_profile_changed +signal active_controller_changed(controller_name: String) +signal controller_input_observed(event: InputEvent) + +const FORMAT_VERSION: int = 1 +const PROFILE_PATH: String = "user://controller_mappings.json" +const PROFILE_TEMP_PATH: String = "user://controller_mappings.json.tmp" +const PROFILE_BACKUP_PATH: String = "user://controller_mappings.json.backup" +const MAX_PROFILE_BYTES: int = 1024 * 1024 +const CAPTURE_AXIS_THRESHOLD: float = 0.55 +const CAPTURE_AXIS_RELEASE_THRESHOLD: float = 0.30 +const ACTIVE_DEVICE_AXIS_THRESHOLD: float = 0.35 +const MUOS_MAPPING_REVISION: String = "muos-v4" +const MUOS_CONTROLLER_NAMES: Array[String] = [ + "muOS-Keys", + "Deeplay-keys", +] +const MUOS_MAPPING_BINDINGS: String = ( + "a:b0,b:b1,x:b3,y:b2," + + "leftshoulder:b4,rightshoulder:b5," + + "lefttrigger:b10,righttrigger:b11," + + "guide:b8,start:b7,back:b6," + + "dpup:h0.1,dpleft:h0.8,dpright:h0.2,dpdown:h0.4," + + "leftx:a0,lefty:a1,leftstick:b9," + + "rightx:a2,righty:a3,rightstick:b12,platform:Linux," +) +const ROLE_A: StringName = &"a" +const ROLE_B: StringName = &"b" +const ROLE_X: StringName = &"x" +const ROLE_Y: StringName = &"y" +const ROLE_LB: StringName = &"lb" +const ROLE_RB: StringName = &"rb" +# Keep the serialized keys for profile compatibility, but name the roles after +# their game actions. A handheld may expose either action as a button, a +# dedicated trigger axis, or one half of a shared axis. +const ROLE_POINTER_MODIFIER: StringName = &"lt" +const ROLE_CAMERA_ZOOM: StringName = &"rt" +const ROLE_LT: StringName = ROLE_POINTER_MODIFIER +const ROLE_RT: StringName = ROLE_CAMERA_ZOOM +const ROLE_SELECT: StringName = &"select" +const ROLE_START: StringName = &"start" +const ROLE_LEFT_STICK_CLICK: StringName = &"left_stick_click" +const ROLE_RIGHT_STICK_CLICK: StringName = &"right_stick_click" +const ROLE_DPAD_UP: StringName = &"dpad_up" +const ROLE_DPAD_DOWN: StringName = &"dpad_down" +const ROLE_DPAD_LEFT: StringName = &"dpad_left" +const ROLE_DPAD_RIGHT: StringName = &"dpad_right" +const ROLE_LEFT_STICK_X: StringName = &"left_stick_x" +const ROLE_LEFT_STICK_Y: StringName = &"left_stick_y" +const ROLE_RIGHT_STICK_X: StringName = &"right_stick_x" +const ROLE_RIGHT_STICK_Y: StringName = &"right_stick_y" + +const ROLE_ORDER: Array[StringName] = [ + ROLE_A, + ROLE_B, + ROLE_X, + ROLE_Y, + ROLE_LB, + ROLE_RB, + ROLE_POINTER_MODIFIER, + ROLE_CAMERA_ZOOM, + ROLE_SELECT, + ROLE_START, + ROLE_LEFT_STICK_CLICK, + ROLE_RIGHT_STICK_CLICK, + ROLE_DPAD_UP, + ROLE_DPAD_DOWN, + ROLE_DPAD_LEFT, + ROLE_DPAD_RIGHT, + ROLE_LEFT_STICK_X, + ROLE_LEFT_STICK_Y, + ROLE_RIGHT_STICK_X, + ROLE_RIGHT_STICK_Y, +] + +const ROLE_LABELS: Dictionary = { + ROLE_A: "jump / menu accept", + ROLE_B: "menu back", + ROLE_X: "player menu", + ROLE_Y: "interact", + ROLE_LB: "focus chat or world", + ROLE_RB: "primary action", + ROLE_POINTER_MODIFIER: "virtual mouse modifier", + ROLE_CAMERA_ZOOM: "camera zoom", + ROLE_SELECT: "chat", + ROLE_START: "pause", + ROLE_LEFT_STICK_CLICK: "sprint", + ROLE_RIGHT_STICK_CLICK: "unassigned stick click", + ROLE_DPAD_UP: "emote wheel", + ROLE_DPAD_DOWN: "quick menu", + ROLE_DPAD_LEFT: "previous hotbar slot", + ROLE_DPAD_RIGHT: "next hotbar slot", + ROLE_LEFT_STICK_X: "move left / right", + ROLE_LEFT_STICK_Y: "move up / down", + ROLE_RIGHT_STICK_X: "camera left / right", + ROLE_RIGHT_STICK_Y: "camera up / down", +} + +const ROLE_PROMPTS: Dictionary = { + ROLE_A: "press the a button", + ROLE_B: "press the b button", + ROLE_X: "press the x button", + ROLE_Y: "press the y button", + ROLE_LB: "press the left bumper", + ROLE_RB: "press the right bumper", + ROLE_POINTER_MODIFIER: "press or squeeze the virtual mouse modifier", + ROLE_CAMERA_ZOOM: "press or squeeze the camera zoom control", + ROLE_SELECT: "press select / back", + ROLE_START: "press start", + ROLE_LEFT_STICK_CLICK: "click the left stick", + ROLE_RIGHT_STICK_CLICK: "click the right stick", + ROLE_DPAD_UP: "press d-pad up", + ROLE_DPAD_DOWN: "press d-pad down", + ROLE_DPAD_LEFT: "press d-pad left", + ROLE_DPAD_RIGHT: "press d-pad right", + ROLE_LEFT_STICK_X: "move the left stick left", + ROLE_LEFT_STICK_Y: "move the left stick up", + ROLE_RIGHT_STICK_X: "move the right stick left", + ROLE_RIGHT_STICK_Y: "move the right stick up", +} + +const STICK_AXIS_ROLES: Array[StringName] = [ + ROLE_LEFT_STICK_X, + ROLE_LEFT_STICK_Y, + ROLE_RIGHT_STICK_X, + ROLE_RIGHT_STICK_Y, +] +const TRIGGER_ROLES: Array[StringName] = [ + ROLE_POINTER_MODIFIER, + ROLE_CAMERA_ZOOM, +] + +const BUTTON_ACTION_ROLES: Dictionary = { + &"jump": ROLE_A, + &"ui_accept": ROLE_A, + &"ui_cancel": ROLE_B, + &"open_backpack": ROLE_X, + &"interact": ROLE_Y, + &"focus_gameplay": ROLE_LB, + &"fish_primary": ROLE_RB, + &"open_chat": ROLE_SELECT, + &"open_system_menu": ROLE_START, + &"sprint": ROLE_LEFT_STICK_CLICK, + &"open_emotes": ROLE_DPAD_UP, + &"open_quick_actions": ROLE_DPAD_DOWN, + &"hotbar_previous": ROLE_DPAD_LEFT, + &"hotbar_next": ROLE_DPAD_RIGHT, +} + +const AXIS_ACTION_ROLES: Dictionary = { + &"move_left": [ROLE_LEFT_STICK_X, -1.0], + &"move_right": [ROLE_LEFT_STICK_X, 1.0], + &"move_forward": [ROLE_LEFT_STICK_Y, -1.0], + &"move_backward": [ROLE_LEFT_STICK_Y, 1.0], + &"ui_left": [ROLE_LEFT_STICK_X, -1.0], + &"ui_right": [ROLE_LEFT_STICK_X, 1.0], + &"ui_up": [ROLE_LEFT_STICK_Y, -1.0], + &"ui_down": [ROLE_LEFT_STICK_Y, 1.0], +} + +const UI_DPAD_ACTION_ROLES: Dictionary = { + &"ui_up": ROLE_DPAD_UP, + &"ui_down": ROLE_DPAD_DOWN, + &"ui_left": ROLE_DPAD_LEFT, + &"ui_right": ROLE_DPAD_RIGHT, +} + +var _profiles: Dictionary = {} +var _active_device_id: int = 0 +var _active_profile_key: String = "default" +var _active_controller_name: String = "controller" +var _default_joy_events: Dictionary = {} +var _axis_rest_by_device: Dictionary = {} +var _installed_compatibility_guids: Dictionary = {} + + +func _ready() -> void: + if not Input.joy_connection_changed.is_connected( + _on_joy_connection_changed + ): + Input.joy_connection_changed.connect(_on_joy_connection_changed) + _install_known_controller_mappings() + for device_id: int in Input.get_connected_joypads(): + _sample_axis_rest_values(device_id) + _capture_project_defaults() + load_profiles() + _refresh_active_controller() + + +func _input(event: InputEvent) -> void: + var device_id: int = -1 + var button_event := event as InputEventJoypadButton + if button_event != null: + device_id = button_event.device + var motion_event := event as InputEventJoypadMotion + if motion_event != null: + device_id = motion_event.device + var claims_active_device: bool = ( + button_event != null and button_event.pressed + ) + if motion_event != null: + claims_active_device = _axis_motion_claims_device(motion_event) + if ( + claims_active_device + and device_id >= 0 + and device_id != _active_device_id + ): + _set_active_controller(device_id) + if button_event != null or motion_event != null: + controller_input_observed.emit(event) + + +func _process(_delta: float) -> void: + var connected: Array[int] = Input.get_connected_joypads() + if connected.is_empty(): + connected.append(0) + if not connected.has(_active_device_id): + _set_active_controller(connected[0]) + elif ( + _active_controller_name == "controller" + and Input.get_connected_joypads().has(_active_device_id) + ): + _set_active_controller(_active_device_id) + _sample_axis_rest_values(_active_device_id) + + +func load_profiles() -> bool: + _recover_interrupted_write() + if not FileAccess.file_exists(PROFILE_PATH): + _profiles = {} + return true + var file := FileAccess.open(PROFILE_PATH, FileAccess.READ) + if file == null: + return false + if file.get_length() > MAX_PROFILE_BYTES: + file.close() + push_warning("Controller mappings are too large; using defaults.") + _profiles = {} + return false + var json := JSON.new() + var error: Error = json.parse(file.get_as_text()) + file.close() + if error != OK or typeof(json.data) != TYPE_DICTIONARY: + push_warning("Controller mappings are malformed; using defaults.") + _profiles = {} + return false + var data: Dictionary = json.data + if int(data.get("format_version", -1)) != FORMAT_VERSION: + push_warning("Controller mapping version is unsupported; using defaults.") + _profiles = {} + return false + var raw_profiles: Variant = data.get("profiles", {}) + if typeof(raw_profiles) != TYPE_DICTIONARY: + _profiles = {} + return false + _profiles = _validated_profiles(raw_profiles as Dictionary) + return true + + +func _validated_profiles(raw_profiles: Dictionary) -> Dictionary: + var validated: Dictionary = {} + for key_value: Variant in raw_profiles: + var raw_profile: Variant = raw_profiles[key_value] + if typeof(raw_profile) != TYPE_DICTIONARY: + continue + var profile := raw_profile as Dictionary + var raw_bindings: Variant = profile.get("bindings", {}) + if typeof(raw_bindings) != TYPE_DICTIONARY: + continue + var bindings := raw_bindings as Dictionary + var complete: bool = true + for role: StringName in ROLE_ORDER: + var raw_binding: Variant = bindings.get(str(role), {}) + if ( + typeof(raw_binding) != TYPE_DICTIONARY + or not validate_binding(role, raw_binding as Dictionary) + ): + complete = false + break + if not complete: + continue + validated[str(key_value)] = { + "controller_name": str( + profile.get("controller_name", "controller") + ), + "bindings": bindings.duplicate(true), + } + return validated + + +func has_custom_mapping() -> bool: + return _profiles.has(_active_profile_key) + + +func get_active_device_id() -> int: + return _active_device_id + + +func get_active_controller_name() -> String: + return _active_controller_name + + +func get_active_controller_guid() -> String: + if not Input.get_connected_joypads().has(_active_device_id): + return "" + return Input.get_joy_guid(_active_device_id).strip_edges() + + +func get_active_profile_key() -> String: + return _active_profile_key + + +func get_active_controller_platform() -> String: + return OS.get_name() + + +func recalibrate_active_device() -> void: + _recalibrate_device(_active_device_id) + + +func get_role_label(role: StringName) -> String: + return str(ROLE_LABELS.get(role, str(role))) + + +func get_role_prompt(role: StringName) -> String: + return str(ROLE_PROMPTS.get(role, "press or move the requested input")) + + +func role_expects_axis(role: StringName) -> bool: + return role in STICK_AXIS_ROLES + + +func role_accepts_axis(role: StringName) -> bool: + return role in STICK_AXIS_ROLES or role in TRIGGER_ROLES + + +func are_capture_inputs_neutral(device_id: int = -1) -> bool: + var target_device: int = ( + _active_device_id if device_id < 0 else device_id + ) + for button_index: int in JOY_BUTTON_MAX: + if Input.is_joy_button_pressed(target_device, button_index): + return false + for axis_index: int in JOY_AXIS_MAX: + var rest: float = _axis_rest_value(target_device, axis_index) + var current: float = Input.get_joy_axis(target_device, axis_index) + if absf(current - rest) > CAPTURE_AXIS_RELEASE_THRESHOLD: + return false + return true + + +func get_pressed_capture_button(device_id: int = -1) -> int: + var target_device: int = ( + _active_device_id if device_id < 0 else device_id + ) + for button_index: int in JOY_BUTTON_MAX: + if Input.is_joy_button_pressed(target_device, button_index): + return button_index + return -1 + + +func get_active_bindings() -> Dictionary: + var raw_profile: Variant = _profiles.get(_active_profile_key, {}) + var profile: Dictionary = ( + raw_profile as Dictionary + if typeof(raw_profile) == TYPE_DICTIONARY else {} + ) + var bindings: Variant = profile.get("bindings", {}) + if ( + typeof(bindings) == TYPE_DICTIONARY + and not (bindings as Dictionary).is_empty() + ): + return (bindings as Dictionary).duplicate(true) + return default_bindings() + + +func get_binding(role: StringName) -> Dictionary: + var bindings: Dictionary = get_active_bindings() + var binding: Variant = bindings.get(str(role), {}) + if typeof(binding) == TYPE_DICTIONARY: + return (binding as Dictionary).duplicate(true) + return {} + + +func set_binding(role: StringName, binding: Dictionary) -> bool: + if role not in ROLE_ORDER or not validate_binding(role, binding): + return false + var bindings: Dictionary = get_active_bindings() + bindings[str(role)] = binding.duplicate(true) + return replace_active_bindings(bindings) + + +func replace_active_bindings(bindings: Dictionary) -> bool: + for role: StringName in ROLE_ORDER: + var binding: Variant = bindings.get(str(role), {}) + if typeof(binding) != TYPE_DICTIONARY: + return false + if not validate_binding(role, binding as Dictionary): + return false + var previous: Dictionary = _profiles.duplicate(true) + _profiles[_active_profile_key] = { + "controller_name": _active_controller_name, + "bindings": bindings.duplicate(true), + } + if not _save_profiles(): + _profiles = previous + return false + _apply_active_profile() + active_profile_changed.emit() + return true + + +func reset_active_profile() -> bool: + if not _profiles.has(_active_profile_key): + _restore_project_defaults() + active_profile_changed.emit() + return true + var previous: Dictionary = _profiles.duplicate(true) + _profiles.erase(_active_profile_key) + if not _save_profiles(): + _profiles = previous + return false + _restore_project_defaults() + active_profile_changed.emit() + return true + + +func binding_from_event(role: StringName, event: InputEvent) -> Dictionary: + var button := event as InputEventJoypadButton + if role in TRIGGER_ROLES and button != null and button.pressed: + return { + "kind": "button", + "button": int(button.button_index), + } + if role_accepts_axis(role): + var motion := event as InputEventJoypadMotion + if motion == null: + return {} + _sample_axis_rest_values(motion.device) + var rest: float = _axis_rest_value(motion.device, motion.axis) + var travel: float = motion.axis_value - rest + if absf(travel) < CAPTURE_AXIS_THRESHOLD: + return {} + return { + "kind": "axis", + "axis": int(motion.axis), + "direction": signf(travel), + "rest": rest, + } + if button == null or not button.pressed: + return {} + return { + "kind": "button", + "button": int(button.button_index), + } + + +func validate_binding(role: StringName, binding: Dictionary) -> bool: + var binding_kind: String = str(binding.get("kind", "")) + if role_expects_axis(role) and binding_kind != "axis": + return false + if role not in STICK_AXIS_ROLES and role not in TRIGGER_ROLES: + if binding_kind != "button": + return false + if role in TRIGGER_ROLES and binding_kind not in ["axis", "button"]: + return false + if binding_kind == "button": + var button_index: int = int(binding.get("button", -1)) + return button_index >= 0 and button_index < JOY_BUTTON_MAX + var axis_index: int = int(binding.get("axis", -1)) + var direction: float = float(binding.get("direction", 0.0)) + var rest: float = float(binding.get("rest", 0.0)) + return ( + axis_index >= 0 + and axis_index < JOY_AXIS_MAX + and not is_zero_approx(direction) + and rest >= -1.0 + and rest <= 1.0 + ) + + +func binding_label(binding: Dictionary) -> String: + if str(binding.get("kind", "")) == "button": + return _button_label(int(binding.get("button", -1))) + if str(binding.get("kind", "")) == "axis": + return "%s %s" % [ + _axis_label(int(binding.get("axis", -1))), + "+" if float(binding.get("direction", 0.0)) > 0.0 else "−", + ] + return "unmapped" + + +static func _button_label(button_index: int) -> String: + match button_index: + JOY_BUTTON_A: + return "A button" + JOY_BUTTON_B: + return "B button" + JOY_BUTTON_X: + return "X button" + JOY_BUTTON_Y: + return "Y button" + JOY_BUTTON_BACK: + return "back / select" + JOY_BUTTON_GUIDE: + return "guide button" + JOY_BUTTON_START: + return "start button" + JOY_BUTTON_LEFT_STICK: + return "left stick click" + JOY_BUTTON_RIGHT_STICK: + return "right stick click" + JOY_BUTTON_LEFT_SHOULDER: + return "left shoulder" + JOY_BUTTON_RIGHT_SHOULDER: + return "right shoulder" + JOY_BUTTON_DPAD_UP: + return "d-pad up" + JOY_BUTTON_DPAD_DOWN: + return "d-pad down" + JOY_BUTTON_DPAD_LEFT: + return "d-pad left" + JOY_BUTTON_DPAD_RIGHT: + return "d-pad right" + _: + return "button %d" % button_index + + +static func _axis_label(axis_index: int) -> String: + match axis_index: + JOY_AXIS_LEFT_X: + return "left stick X" + JOY_AXIS_LEFT_Y: + return "left stick Y" + JOY_AXIS_RIGHT_X: + return "right stick X" + JOY_AXIS_RIGHT_Y: + return "right stick Y" + JOY_AXIS_TRIGGER_LEFT: + return "left trigger axis" + JOY_AXIS_TRIGGER_RIGHT: + return "right trigger axis" + _: + return "axis %d" % axis_index + + +func get_role_strength(role: StringName) -> float: + var binding: Dictionary = get_binding(role) + if str(binding.get("kind", "")) == "button": + return ( + 1.0 + if Input.is_joy_button_pressed( + _active_device_id, + int(binding.get("button", -1)), + ) + else 0.0 + ) + return _axis_binding_strength(binding) + + +func get_role_axis(role: StringName) -> float: + var binding: Dictionary = get_binding(role) + if str(binding.get("kind", "")) != "axis": + return 0.0 + var raw: float = Input.get_joy_axis( + _active_device_id, + int(binding.get("axis", -1)), + ) + var direction: float = signf(float(binding.get("direction", -1.0))) + var rest: float = float(binding.get("rest", 0.0)) + var delta: float = raw - rest + if is_zero_approx(delta): + return 0.0 + var toward_captured_direction: bool = signf(delta) == direction + var endpoint: float = direction if toward_captured_direction else -direction + var available: float = absf(endpoint - rest) + if available <= 0.001: + return 0.0 + var magnitude: float = clampf(absf(delta) / available, 0.0, 1.0) + return -magnitude if toward_captured_direction else magnitude + + +func event_matches_role(event: InputEvent, role: StringName) -> bool: + if not _event_belongs_to_active_device(event): + return false + var binding: Dictionary = get_binding(role) + var button := event as InputEventJoypadButton + if button != null and str(binding.get("kind", "")) == "button": + return button.button_index == int(binding.get("button", -1)) + var motion := event as InputEventJoypadMotion + if motion == null or str(binding.get("kind", "")) != "axis": + return false + if motion.axis != int(binding.get("axis", -1)): + return false + return _axis_binding_strength(binding, motion.axis_value) >= 0.5 + + +func event_uses_role(event: InputEvent, role: StringName) -> bool: + if not _event_belongs_to_active_device(event): + return false + var binding: Dictionary = get_binding(role) + var button := event as InputEventJoypadButton + if button != null and str(binding.get("kind", "")) == "button": + return button.button_index == int(binding.get("button", -1)) + var motion := event as InputEventJoypadMotion + return ( + motion != null + and str(binding.get("kind", "")) == "axis" + and motion.axis == int(binding.get("axis", -1)) + ) + + +static func default_bindings() -> Dictionary: + return { + str(ROLE_A): _button_binding(JOY_BUTTON_A), + str(ROLE_B): _button_binding(JOY_BUTTON_B), + str(ROLE_X): _button_binding(JOY_BUTTON_X), + str(ROLE_Y): _button_binding(JOY_BUTTON_Y), + str(ROLE_LB): _button_binding(JOY_BUTTON_LEFT_SHOULDER), + str(ROLE_RB): _button_binding(JOY_BUTTON_RIGHT_SHOULDER), + str(ROLE_POINTER_MODIFIER): _axis_binding( + JOY_AXIS_TRIGGER_RIGHT, 1.0, 0.0 + ), + str(ROLE_CAMERA_ZOOM): _axis_binding( + JOY_AXIS_TRIGGER_LEFT, 1.0, 0.0 + ), + str(ROLE_SELECT): _button_binding(JOY_BUTTON_BACK), + str(ROLE_START): _button_binding(JOY_BUTTON_START), + str(ROLE_LEFT_STICK_CLICK): _button_binding(JOY_BUTTON_LEFT_STICK), + str(ROLE_RIGHT_STICK_CLICK): _button_binding(JOY_BUTTON_RIGHT_STICK), + str(ROLE_DPAD_UP): _button_binding(JOY_BUTTON_DPAD_UP), + str(ROLE_DPAD_DOWN): _button_binding(JOY_BUTTON_DPAD_DOWN), + str(ROLE_DPAD_LEFT): _button_binding(JOY_BUTTON_DPAD_LEFT), + str(ROLE_DPAD_RIGHT): _button_binding(JOY_BUTTON_DPAD_RIGHT), + str(ROLE_LEFT_STICK_X): _axis_binding(JOY_AXIS_LEFT_X, -1.0, 0.0), + str(ROLE_LEFT_STICK_Y): _axis_binding(JOY_AXIS_LEFT_Y, -1.0, 0.0), + str(ROLE_RIGHT_STICK_X): _axis_binding(JOY_AXIS_RIGHT_X, -1.0, 0.0), + str(ROLE_RIGHT_STICK_Y): _axis_binding(JOY_AXIS_RIGHT_Y, -1.0, 0.0), + } + + +static func _button_binding(button: JoyButton) -> Dictionary: + return {"kind": "button", "button": int(button)} + + +static func _axis_binding( + axis: JoyAxis, + direction: float, + rest: float, +) -> Dictionary: + return { + "kind": "axis", + "axis": int(axis), + "direction": direction, + "rest": rest, + } + + +func _axis_binding_strength( + binding: Dictionary, + raw_override: float = INF, +) -> float: + var axis: int = int(binding.get("axis", -1)) + if axis < 0: + return 0.0 + var raw: float = ( + Input.get_joy_axis(_active_device_id, axis) + if is_inf(raw_override) else raw_override + ) + var rest: float = float(binding.get("rest", 0.0)) + var direction: float = signf(float(binding.get("direction", 0.0))) + var endpoint: float = direction + var available: float = absf(endpoint - rest) + if available <= 0.001: + return 0.0 + return clampf((raw - rest) * direction / available, 0.0, 1.0) + + +func _refresh_active_controller() -> void: + var connected: Array[int] = Input.get_connected_joypads() + if connected.has(_active_device_id): + _set_active_controller(_active_device_id) + return + _set_active_controller(connected[0] if not connected.is_empty() else 0) + + +func _on_joy_connection_changed(device_id: int, connected: bool) -> void: + _axis_rest_by_device.erase(str(device_id)) + if connected: + _install_known_controller_mapping(device_id) + call_deferred("_recalibrate_device", device_id) + _refresh_active_controller() + + +func _install_known_controller_mappings() -> void: + for device_id: int in Input.get_connected_joypads(): + _install_known_controller_mapping(device_id) + + +func _install_known_controller_mapping(device_id: int) -> bool: + var controller_name: String = Input.get_joy_name(device_id).strip_edges() + if not should_install_muos_compatibility_mapping( + controller_name, + Input.is_joy_known(device_id), + ): + return false + var guid: String = Input.get_joy_guid(device_id).strip_edges() + if guid.is_empty(): + return false + if _installed_compatibility_guids.has(guid): + return true + _installed_compatibility_guids[guid] = true + Input.add_joy_mapping( + build_muos_controller_mapping(guid, controller_name), + true, + ) + return true + + +static func build_muos_controller_mapping( + guid: String, + controller_name: String, +) -> String: + return "%s,%s,%s" % [ + guid.strip_edges(), + controller_name.strip_edges(), + MUOS_MAPPING_BINDINGS, + ] + + +static func is_muos_controller_name(controller_name: String) -> bool: + return controller_name.strip_edges() in MUOS_CONTROLLER_NAMES + + +static func should_install_muos_compatibility_mapping( + controller_name: String, + is_known: bool, +) -> bool: + # Never update an already-recognized virtual controller at runtime. Godot + # can stop delivering its standardized input events after that replacement. + return is_muos_controller_name(controller_name) and not is_known + + +func _set_active_controller(device_id: int) -> void: + var previous_device_id: int = _active_device_id + var previous_profile_key: String = _active_profile_key + var previous_controller_name: String = _active_controller_name + _active_device_id = maxi(device_id, 0) + var controller_is_listed: bool = Input.get_connected_joypads().has( + _active_device_id + ) + var guid: String = ( + Input.get_joy_guid(_active_device_id).strip_edges() + if controller_is_listed else "" + ) + var controller_name: String = ( + Input.get_joy_name(_active_device_id).strip_edges() + if controller_is_listed else "controller" + ) + if controller_name.is_empty(): + controller_name = "controller" + _active_controller_name = controller_name + if guid.is_empty(): + _active_profile_key = "name:" + controller_name + elif is_muos_controller_name(controller_name): + _active_profile_key = guid + ":" + MUOS_MAPPING_REVISION + else: + _active_profile_key = guid + if ( + previous_profile_key == "name:controller" + and _active_profile_key != previous_profile_key + and _profiles.has(previous_profile_key) + and not _profiles.has(_active_profile_key) + ): + var previous_profile: Variant = _profiles[previous_profile_key] + if typeof(previous_profile) == TYPE_DICTIONARY: + _profiles[_active_profile_key] = ( + previous_profile as Dictionary + ).duplicate(true) + _profiles.erase(previous_profile_key) + _save_profiles() + _sample_axis_rest_values(_active_device_id) + _apply_active_profile() + if ( + previous_device_id != _active_device_id + or previous_profile_key != _active_profile_key + or previous_controller_name != _active_controller_name + ): + active_controller_changed.emit(_active_controller_name) + + +func _axis_motion_claims_device(event: InputEventJoypadMotion) -> bool: + var device_key: String = str(event.device) + if not _axis_rest_by_device.has(device_key): + _sample_axis_rest_values(event.device) + return false + var rest: float = _axis_rest_value(event.device, event.axis) + return absf(event.axis_value - rest) >= ACTIVE_DEVICE_AXIS_THRESHOLD + + +func _event_belongs_to_active_device(event: InputEvent) -> bool: + var button := event as InputEventJoypadButton + if button != null: + return button.device == _active_device_id + var motion := event as InputEventJoypadMotion + return motion != null and motion.device == _active_device_id + + +func _sample_axis_rest_values(device_id: int) -> void: + var key: String = str(device_id) + if _axis_rest_by_device.has(key): + return + var values: Dictionary = {} + for axis: int in JOY_AXIS_MAX: + values[str(axis)] = Input.get_joy_axis(device_id, axis) + _axis_rest_by_device[key] = values + + +func _recalibrate_device(device_id: int) -> void: + _axis_rest_by_device.erase(str(device_id)) + var connected: Array[int] = Input.get_connected_joypads() + if not connected.has(device_id) and not ( + connected.is_empty() and device_id == 0 + ): + return + _sample_axis_rest_values(device_id) + + +func _axis_rest_value(device_id: int, axis: int) -> float: + _sample_axis_rest_values(device_id) + var values: Dictionary = _axis_rest_by_device.get(str(device_id), {}) + return float(values.get(str(axis), 0.0)) + + +func _capture_project_defaults() -> void: + for action: StringName in _all_managed_actions(): + var joy_events: Array[InputEvent] = [] + for event: InputEvent in InputMap.action_get_events(action): + if event is InputEventJoypadButton or event is InputEventJoypadMotion: + joy_events.append(event.duplicate()) + _default_joy_events[action] = joy_events + + +func _all_managed_actions() -> Array[StringName]: + var result: Array[StringName] = [] + for source: Dictionary in [ + BUTTON_ACTION_ROLES, + AXIS_ACTION_ROLES, + UI_DPAD_ACTION_ROLES, + ]: + for action_value: Variant in source.keys(): + var action: StringName = StringName(action_value) + if action not in result: + result.append(action) + return result + + +func _remove_managed_joy_events() -> void: + for action: StringName in _all_managed_actions(): + if not InputMap.has_action(action): + continue + for event: InputEvent in InputMap.action_get_events(action): + if event is InputEventJoypadButton or event is InputEventJoypadMotion: + InputMap.action_erase_event(action, event) + + +func _restore_project_defaults() -> void: + _remove_managed_joy_events() + for action_value: Variant in _default_joy_events: + var action: StringName = StringName(action_value) + for event: InputEvent in _default_joy_events[action]: + var active_event: InputEvent = event.duplicate() + active_event.device = _active_device_id + InputMap.action_add_event(action, active_event) + + +func _apply_active_profile() -> void: + if not has_custom_mapping(): + _restore_project_defaults() + return + _remove_managed_joy_events() + var bindings: Dictionary = get_active_bindings() + for action_value: Variant in BUTTON_ACTION_ROLES: + var action: StringName = StringName(action_value) + _add_button_action_binding( + action, + bindings.get(str(BUTTON_ACTION_ROLES[action]), {}), + ) + for action_value: Variant in AXIS_ACTION_ROLES: + var action: StringName = StringName(action_value) + var role_data: Array = AXIS_ACTION_ROLES[action] + _add_axis_action_binding( + action, + bindings.get(str(role_data[0]), {}), + float(role_data[1]), + ) + for action_value: Variant in UI_DPAD_ACTION_ROLES: + var action: StringName = StringName(action_value) + _add_button_action_binding( + action, + bindings.get(str(UI_DPAD_ACTION_ROLES[action]), {}), + ) + + +func _add_button_action_binding(action: StringName, value: Variant) -> void: + if not InputMap.has_action(action) or typeof(value) != TYPE_DICTIONARY: + return + var binding: Dictionary = value as Dictionary + if str(binding.get("kind", "")) != "button": + return + var event := InputEventJoypadButton.new() + event.device = _active_device_id + event.button_index = int(binding.get("button", -1)) as JoyButton + InputMap.action_add_event(action, event) + + +func _add_axis_action_binding( + action: StringName, + value: Variant, + logical_direction: float, +) -> void: + if not InputMap.has_action(action) or typeof(value) != TYPE_DICTIONARY: + return + var binding: Dictionary = value as Dictionary + if str(binding.get("kind", "")) != "axis": + return + var captured_negative_direction: float = signf( + float(binding.get("direction", -1.0)) + ) + var event := InputEventJoypadMotion.new() + event.device = _active_device_id + event.axis = int(binding.get("axis", -1)) as JoyAxis + event.axis_value = logical_direction * -captured_negative_direction + InputMap.action_add_event(action, event) + + +func _save_profiles() -> bool: + var file := FileAccess.open(PROFILE_TEMP_PATH, FileAccess.WRITE) + if file == null: + return false + file.store_string(JSON.stringify({ + "format_version": FORMAT_VERSION, + "profiles": _profiles, + }, "\t")) + file.flush() + var write_error: Error = file.get_error() + file.close() + if write_error != OK: + _remove_if_present(PROFILE_TEMP_PATH) + return false + _remove_if_present(PROFILE_BACKUP_PATH) + var had_primary: bool = FileAccess.file_exists(PROFILE_PATH) + if had_primary and not _rename_file(PROFILE_PATH, PROFILE_BACKUP_PATH): + _remove_if_present(PROFILE_TEMP_PATH) + return false + if not _rename_file(PROFILE_TEMP_PATH, PROFILE_PATH): + if had_primary: + _rename_file(PROFILE_BACKUP_PATH, PROFILE_PATH) + return false + _remove_if_present(PROFILE_BACKUP_PATH) + return true + + +func _recover_interrupted_write() -> void: + if FileAccess.file_exists(PROFILE_PATH): + _remove_if_present(PROFILE_TEMP_PATH) + _remove_if_present(PROFILE_BACKUP_PATH) + return + if FileAccess.file_exists(PROFILE_BACKUP_PATH): + _rename_file(PROFILE_BACKUP_PATH, PROFILE_PATH) + _remove_if_present(PROFILE_TEMP_PATH) + + +func _rename_file(from_path: String, to_path: String) -> bool: + return DirAccess.rename_absolute( + ProjectSettings.globalize_path(from_path), + ProjectSettings.globalize_path(to_path), + ) == OK + + +func _remove_if_present(path: String) -> void: + if FileAccess.file_exists(path): + DirAccess.remove_absolute(ProjectSettings.globalize_path(path)) diff --git a/settings/controller_mapping_manager.gd.uid b/settings/controller_mapping_manager.gd.uid new file mode 100644 index 0000000..3fcbc13 --- /dev/null +++ b/settings/controller_mapping_manager.gd.uid @@ -0,0 +1 @@ +uid://c80x8jkdnx0xy diff --git a/settings/player_settings.gd b/settings/player_settings.gd index c83e534..a20a3e1 100644 --- a/settings/player_settings.gd +++ b/settings/player_settings.gd @@ -7,13 +7,15 @@ const MIN_MOUSE_SENSITIVITY: float = 0.001 const MAX_MOUSE_SENSITIVITY: float = 0.012 const MIN_CONTROLLER_SENSITIVITY: float = 0.5 const MAX_CONTROLLER_SENSITIVITY: float = 5.0 +const MIN_AUDIO_VOLUME: float = 0.0 +const MAX_AUDIO_VOLUME: float = 1.0 const MIN_WORLD_PIXEL_SIZE: int = 1 const MAX_WORLD_PIXEL_SIZE: int = 5 const DEFAULT_WORLD_PIXEL_SIZE: int = 3 const MIN_UI_PIXEL_SIZE: int = 1 const MAX_UI_PIXEL_SIZE: int = 5 const DEFAULT_UI_PIXEL_SIZE: int = 3 -const MAX_CHAT_DRAFT_CHARACTERS: int = 280 +const MAX_CHAT_DRAFT_CHARACTERS: int = 256 const COMPACT_DISPLAY_HEIGHT: int = 560 const WORLD_DESKTOP_GRID_HEIGHTS: Array[int] = [0, 540, 360, 216, 96] const WORLD_COMPACT_GRID_HEIGHTS: Array[int] = [0, 360, 240, 144, 72] @@ -27,10 +29,17 @@ const UI_COMPACT_RENDER_HEIGHTS: Array[int] = [0, 408, 336, 264, 192] @export_range(0.001, 0.012, 0.0005) var mouse_camera_sensitivity: float = 0.005 @export_range(0.5, 5.0, 0.1) var controller_camera_sensitivity: float = 2.5 @export var invert_camera_y: bool = false +@export var on_screen_keyboard_enabled: bool = false @export var chat_draft: String = "" @export var chat_collapsed: bool = false @export var chat_dock_right: bool = false +@export var chat_mobile_mode: bool = false @export var paint_dock_right: bool = true +@export var fullscreen_enabled: bool = false +@export_range(0.0, 1.0, 0.01) var master_volume: float = 1.0 +@export_range(0.0, 1.0, 0.01) var music_volume: float = 1.0 +@export_range(0.0, 1.0, 0.01) var effects_volume: float = 1.0 +@export_range(0.0, 1.0, 0.01) var environment_volume: float = 1.0 @export_range(1, 5, 1) var world_pixel_size: int = DEFAULT_WORLD_PIXEL_SIZE @export_range(1, 5, 1) var ui_pixel_size: int = DEFAULT_UI_PIXEL_SIZE @@ -46,6 +55,10 @@ func is_valid() -> bool: and is_finite(controller_camera_sensitivity) and controller_camera_sensitivity >= MIN_CONTROLLER_SENSITIVITY and controller_camera_sensitivity <= MAX_CONTROLLER_SENSITIVITY + and _is_valid_audio_volume(master_volume) + and _is_valid_audio_volume(music_volume) + and _is_valid_audio_volume(effects_volume) + and _is_valid_audio_volume(environment_volume) and chat_draft.length() <= MAX_CHAT_DRAFT_CHARACTERS and world_pixel_size >= MIN_WORLD_PIXEL_SIZE and world_pixel_size <= MAX_WORLD_PIXEL_SIZE @@ -62,15 +75,30 @@ func copy() -> PlayerSettings: result.mouse_camera_sensitivity = mouse_camera_sensitivity result.controller_camera_sensitivity = controller_camera_sensitivity result.invert_camera_y = invert_camera_y + result.on_screen_keyboard_enabled = on_screen_keyboard_enabled result.chat_draft = chat_draft result.chat_collapsed = chat_collapsed result.chat_dock_right = chat_dock_right + result.chat_mobile_mode = chat_mobile_mode result.paint_dock_right = paint_dock_right + result.fullscreen_enabled = fullscreen_enabled + result.master_volume = master_volume + result.music_volume = music_volume + result.effects_volume = effects_volume + result.environment_volume = environment_volume result.world_pixel_size = world_pixel_size result.ui_pixel_size = ui_pixel_size return result +static func _is_valid_audio_volume(value: float) -> bool: + return ( + is_finite(value) + and value >= MIN_AUDIO_VOLUME + and value <= MAX_AUDIO_VOLUME + ) + + static func get_world_grid_height( pixel_size: int, displayed_height: int, diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd index acaf23d..84b3f44 100644 --- a/settings/player_settings_manager.gd +++ b/settings/player_settings_manager.gd @@ -5,6 +5,7 @@ const SETTINGS_VERSION: int = 1 const SETTINGS_PATH: String = "user://player_settings.json" const TEMP_PATH: String = "user://player_settings.json.tmp" const BACKUP_PATH: String = "user://player_settings.json.backup" +const SILENT_VOLUME_DB: float = -80.0 signal settings_changed(settings: PlayerSettings) @@ -16,12 +17,14 @@ func load_settings() -> bool: _recover_interrupted_write() if not FileAccess.file_exists(SETTINGS_PATH): current_settings = PlayerSettings.new() + _apply_audio_settings(current_settings) emit_signal("settings_changed", current_settings) return true var file := FileAccess.open(SETTINGS_PATH, FileAccess.READ) if file == null: push_warning("Unable to open player settings; using defaults.") current_settings = PlayerSettings.new() + _apply_audio_settings(current_settings) emit_signal("settings_changed", current_settings) return false var json := JSON.new() @@ -42,9 +45,16 @@ func load_settings() -> bool: var presentation: Dictionary = {} if typeof(data.get("presentation")) == TYPE_DICTIONARY: presentation = data["presentation"] + var audio: Dictionary = {} + if typeof(data.get("audio")) == TYPE_DICTIONARY: + audio = data["audio"] if ( typeof(accessibility.get("auto_click_enabled")) != TYPE_BOOL or typeof(camera.get("invert_vertical")) != TYPE_BOOL + or ( + accessibility.has("on_screen_keyboard_enabled") + and typeof(accessibility["on_screen_keyboard_enabled"]) != TYPE_BOOL + ) or ( accessibility.has("use_readable_interface_font") and typeof(accessibility["use_readable_interface_font"]) != TYPE_BOOL @@ -61,10 +71,18 @@ func load_settings() -> bool: presentation.has("chat_dock_right") and typeof(presentation["chat_dock_right"]) != TYPE_BOOL ) + or ( + presentation.has("chat_mobile_mode") + and typeof(presentation["chat_mobile_mode"]) != TYPE_BOOL + ) or ( presentation.has("paint_dock_right") and typeof(presentation["paint_dock_right"]) != TYPE_BOOL ) + or ( + presentation.has("fullscreen_enabled") + and typeof(presentation["fullscreen_enabled"]) != TYPE_BOOL + ) ): return _use_defaults_after_corruption("Player settings values are invalid.") var loaded := PlayerSettings.new() @@ -84,6 +102,9 @@ func load_settings() -> bool: -1.0 ) loaded.invert_camera_y = camera["invert_vertical"] + loaded.on_screen_keyboard_enabled = bool( + accessibility.get("on_screen_keyboard_enabled", false) + ) loaded.world_pixel_size = _read_clamped_integer( presentation.get( "world_pixel_size", @@ -107,11 +128,24 @@ func load_settings() -> bool: ) loaded.chat_collapsed = bool(presentation.get("chat_collapsed", false)) loaded.chat_dock_right = bool(presentation.get("chat_dock_right", false)) + loaded.chat_mobile_mode = bool( + presentation.get("chat_mobile_mode", false) + ) loaded.paint_dock_right = bool(presentation.get("paint_dock_right", true)) + loaded.fullscreen_enabled = bool( + presentation.get("fullscreen_enabled", false) + ) + loaded.master_volume = _read_float(audio.get("master", 1.0), 1.0) + loaded.music_volume = _read_float(audio.get("music", 1.0), 1.0) + loaded.effects_volume = _read_float(audio.get("effects", 1.0), 1.0) + loaded.environment_volume = _read_float( + audio.get("environment", 1.0), 1.0 + ) if not loaded.is_valid(): return _use_defaults_after_corruption("Player settings values are invalid.") current_settings = loaded _is_dirty = false + _apply_audio_settings(current_settings) emit_signal("settings_changed", current_settings) return true @@ -124,7 +158,9 @@ func apply_settings(settings: PlayerSettings) -> bool: _is_dirty = true if not save_now(): current_settings = previous + _apply_audio_settings(current_settings) return false + _apply_audio_settings(current_settings) emit_signal("settings_changed", current_settings) return true @@ -169,19 +205,30 @@ func save_now() -> bool: "auto_click_enabled": current_settings.auto_click_enabled, "auto_click_interval": current_settings.auto_click_interval, "use_readable_interface_font": true, + "on_screen_keyboard_enabled": ( + current_settings.on_screen_keyboard_enabled + ), }, "camera": { "mouse_sensitivity": current_settings.mouse_camera_sensitivity, "controller_sensitivity": current_settings.controller_camera_sensitivity, "invert_vertical": current_settings.invert_camera_y, }, + "audio": { + "master": current_settings.master_volume, + "music": current_settings.music_volume, + "effects": current_settings.effects_volume, + "environment": current_settings.environment_volume, + }, "presentation": { "world_pixel_size": current_settings.world_pixel_size, "ui_pixel_size": current_settings.ui_pixel_size, "chat_draft": current_settings.chat_draft, "chat_collapsed": current_settings.chat_collapsed, "chat_dock_right": current_settings.chat_dock_right, + "chat_mobile_mode": current_settings.chat_mobile_mode, "paint_dock_right": current_settings.paint_dock_right, + "fullscreen_enabled": current_settings.fullscreen_enabled, }, } if current_settings.chat_draft.is_empty(): @@ -214,6 +261,22 @@ func save_if_dirty() -> bool: return not _is_dirty or save_now() +func preview_audio_levels( + master: float, + music: float, + effects: float, + environment: float, +) -> void: + _set_bus_volume(&"Master", master) + _set_bus_volume(&"Music", music) + _set_bus_volume(&"SFX", effects) + _set_bus_volume(&"Environment", environment) + + +func restore_audio_levels() -> void: + _apply_audio_settings(current_settings) + + func _exit_tree() -> void: save_if_dirty() @@ -222,10 +285,38 @@ func _use_defaults_after_corruption(message: String) -> bool: push_warning(message) current_settings = PlayerSettings.new() _is_dirty = false + _apply_audio_settings(current_settings) emit_signal("settings_changed", current_settings) return false +func _apply_audio_settings(settings: PlayerSettings) -> void: + preview_audio_levels( + settings.master_volume, + settings.music_volume, + settings.effects_volume, + settings.environment_volume, + ) + + +func _set_bus_volume(bus_name: StringName, linear_volume: float) -> void: + var bus_index: int = AudioServer.get_bus_index(bus_name) + if bus_index < 0: + return + var resolved_volume: float = clampf( + linear_volume, + PlayerSettings.MIN_AUDIO_VOLUME, + PlayerSettings.MAX_AUDIO_VOLUME, + ) + AudioServer.set_bus_mute(bus_index, resolved_volume <= 0.0) + AudioServer.set_bus_volume_db( + bus_index, + SILENT_VOLUME_DB + if resolved_volume <= 0.0 + else linear_to_db(resolved_volume), + ) + + func _recover_interrupted_write() -> void: if FileAccess.file_exists(SETTINGS_PATH): _remove_if_present(TEMP_PATH) diff --git a/sound/dialogue/animalese/placeholder/a.wav b/sound/dialogue/animalese/placeholder/a.wav new file mode 100644 index 0000000..ca652d4 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/a.wav differ diff --git a/sound/dialogue/animalese/placeholder/a.wav.import b/sound/dialogue/animalese/placeholder/a.wav.import new file mode 100644 index 0000000..86de7ca --- /dev/null +++ b/sound/dialogue/animalese/placeholder/a.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dvajjy4bwadio" +path="res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/a.wav" +dest_files=["res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/b.wav b/sound/dialogue/animalese/placeholder/b.wav new file mode 100644 index 0000000..71ceb88 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/b.wav differ diff --git a/sound/dialogue/animalese/placeholder/b.wav.import b/sound/dialogue/animalese/placeholder/b.wav.import new file mode 100644 index 0000000..4aea49c --- /dev/null +++ b/sound/dialogue/animalese/placeholder/b.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cs30tdyuo6wtc" +path="res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/b.wav" +dest_files=["res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/c.wav b/sound/dialogue/animalese/placeholder/c.wav new file mode 100644 index 0000000..8b5eb64 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/c.wav differ diff --git a/sound/dialogue/animalese/placeholder/c.wav.import b/sound/dialogue/animalese/placeholder/c.wav.import new file mode 100644 index 0000000..bbd8074 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/c.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://2inox7x31xam" +path="res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/c.wav" +dest_files=["res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/d.wav b/sound/dialogue/animalese/placeholder/d.wav new file mode 100644 index 0000000..978009a Binary files /dev/null and b/sound/dialogue/animalese/placeholder/d.wav differ diff --git a/sound/dialogue/animalese/placeholder/d.wav.import b/sound/dialogue/animalese/placeholder/d.wav.import new file mode 100644 index 0000000..ffc5bb9 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/d.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://yvha8eukw6l7" +path="res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/d.wav" +dest_files=["res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/e.wav b/sound/dialogue/animalese/placeholder/e.wav new file mode 100644 index 0000000..76f7b8c Binary files /dev/null and b/sound/dialogue/animalese/placeholder/e.wav differ diff --git a/sound/dialogue/animalese/placeholder/e.wav.import b/sound/dialogue/animalese/placeholder/e.wav.import new file mode 100644 index 0000000..49de309 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/e.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://8ujopf8aqs83" +path="res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/e.wav" +dest_files=["res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/f.wav b/sound/dialogue/animalese/placeholder/f.wav new file mode 100644 index 0000000..bec92c1 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/f.wav differ diff --git a/sound/dialogue/animalese/placeholder/f.wav.import b/sound/dialogue/animalese/placeholder/f.wav.import new file mode 100644 index 0000000..dc85ee3 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/f.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c0gv4cphnfcxf" +path="res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/f.wav" +dest_files=["res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/fallback.wav b/sound/dialogue/animalese/placeholder/fallback.wav new file mode 100644 index 0000000..63a759e Binary files /dev/null and b/sound/dialogue/animalese/placeholder/fallback.wav differ diff --git a/sound/dialogue/animalese/placeholder/fallback.wav.import b/sound/dialogue/animalese/placeholder/fallback.wav.import new file mode 100644 index 0000000..e116a3c --- /dev/null +++ b/sound/dialogue/animalese/placeholder/fallback.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bw0pod4tmexm0" +path="res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/fallback.wav" +dest_files=["res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/g.wav b/sound/dialogue/animalese/placeholder/g.wav new file mode 100644 index 0000000..39dccf1 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/g.wav differ diff --git a/sound/dialogue/animalese/placeholder/g.wav.import b/sound/dialogue/animalese/placeholder/g.wav.import new file mode 100644 index 0000000..1709852 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/g.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c7706p0fc8y7c" +path="res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/g.wav" +dest_files=["res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/h.wav b/sound/dialogue/animalese/placeholder/h.wav new file mode 100644 index 0000000..acebfed Binary files /dev/null and b/sound/dialogue/animalese/placeholder/h.wav differ diff --git a/sound/dialogue/animalese/placeholder/h.wav.import b/sound/dialogue/animalese/placeholder/h.wav.import new file mode 100644 index 0000000..96b2359 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/h.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cl66wka105xmo" +path="res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/h.wav" +dest_files=["res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/i.wav b/sound/dialogue/animalese/placeholder/i.wav new file mode 100644 index 0000000..fbd10e1 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/i.wav differ diff --git a/sound/dialogue/animalese/placeholder/i.wav.import b/sound/dialogue/animalese/placeholder/i.wav.import new file mode 100644 index 0000000..0c11fd6 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/i.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://jicgdbnyt6bm" +path="res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/i.wav" +dest_files=["res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/j.wav b/sound/dialogue/animalese/placeholder/j.wav new file mode 100644 index 0000000..0087db5 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/j.wav differ diff --git a/sound/dialogue/animalese/placeholder/j.wav.import b/sound/dialogue/animalese/placeholder/j.wav.import new file mode 100644 index 0000000..46a0baf --- /dev/null +++ b/sound/dialogue/animalese/placeholder/j.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cji2evr0u3m6c" +path="res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/j.wav" +dest_files=["res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/k.wav b/sound/dialogue/animalese/placeholder/k.wav new file mode 100644 index 0000000..c1b5e69 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/k.wav differ diff --git a/sound/dialogue/animalese/placeholder/k.wav.import b/sound/dialogue/animalese/placeholder/k.wav.import new file mode 100644 index 0000000..4af149c --- /dev/null +++ b/sound/dialogue/animalese/placeholder/k.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://eypaqf1iaq5" +path="res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/k.wav" +dest_files=["res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/l.wav b/sound/dialogue/animalese/placeholder/l.wav new file mode 100644 index 0000000..fe2624b Binary files /dev/null and b/sound/dialogue/animalese/placeholder/l.wav differ diff --git a/sound/dialogue/animalese/placeholder/l.wav.import b/sound/dialogue/animalese/placeholder/l.wav.import new file mode 100644 index 0000000..8193ecf --- /dev/null +++ b/sound/dialogue/animalese/placeholder/l.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bixgnaq4idkhj" +path="res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/l.wav" +dest_files=["res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/m.wav b/sound/dialogue/animalese/placeholder/m.wav new file mode 100644 index 0000000..4660d96 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/m.wav differ diff --git a/sound/dialogue/animalese/placeholder/m.wav.import b/sound/dialogue/animalese/placeholder/m.wav.import new file mode 100644 index 0000000..2a2439b --- /dev/null +++ b/sound/dialogue/animalese/placeholder/m.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dan4o17oreugv" +path="res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/m.wav" +dest_files=["res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/n.wav b/sound/dialogue/animalese/placeholder/n.wav new file mode 100644 index 0000000..97e5457 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/n.wav differ diff --git a/sound/dialogue/animalese/placeholder/n.wav.import b/sound/dialogue/animalese/placeholder/n.wav.import new file mode 100644 index 0000000..f5e432c --- /dev/null +++ b/sound/dialogue/animalese/placeholder/n.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dxup6pw80km61" +path="res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/n.wav" +dest_files=["res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/o.wav b/sound/dialogue/animalese/placeholder/o.wav new file mode 100644 index 0000000..bee6bc1 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/o.wav differ diff --git a/sound/dialogue/animalese/placeholder/o.wav.import b/sound/dialogue/animalese/placeholder/o.wav.import new file mode 100644 index 0000000..b094a9f --- /dev/null +++ b/sound/dialogue/animalese/placeholder/o.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://lntkug4avr2k" +path="res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/o.wav" +dest_files=["res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/p.wav b/sound/dialogue/animalese/placeholder/p.wav new file mode 100644 index 0000000..29fdd60 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/p.wav differ diff --git a/sound/dialogue/animalese/placeholder/p.wav.import b/sound/dialogue/animalese/placeholder/p.wav.import new file mode 100644 index 0000000..a431c1f --- /dev/null +++ b/sound/dialogue/animalese/placeholder/p.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://crubsdrd220eh" +path="res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/p.wav" +dest_files=["res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/q.wav b/sound/dialogue/animalese/placeholder/q.wav new file mode 100644 index 0000000..df184ca Binary files /dev/null and b/sound/dialogue/animalese/placeholder/q.wav differ diff --git a/sound/dialogue/animalese/placeholder/q.wav.import b/sound/dialogue/animalese/placeholder/q.wav.import new file mode 100644 index 0000000..574c906 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/q.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ct0decr76agoq" +path="res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/q.wav" +dest_files=["res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/r.wav b/sound/dialogue/animalese/placeholder/r.wav new file mode 100644 index 0000000..41fda60 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/r.wav differ diff --git a/sound/dialogue/animalese/placeholder/r.wav.import b/sound/dialogue/animalese/placeholder/r.wav.import new file mode 100644 index 0000000..1f196b1 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/r.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dpq7rin4pr1n3" +path="res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/r.wav" +dest_files=["res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/s.wav b/sound/dialogue/animalese/placeholder/s.wav new file mode 100644 index 0000000..f9c542a Binary files /dev/null and b/sound/dialogue/animalese/placeholder/s.wav differ diff --git a/sound/dialogue/animalese/placeholder/s.wav.import b/sound/dialogue/animalese/placeholder/s.wav.import new file mode 100644 index 0000000..a794fdf --- /dev/null +++ b/sound/dialogue/animalese/placeholder/s.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c00meumj2blfa" +path="res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/s.wav" +dest_files=["res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/t.wav b/sound/dialogue/animalese/placeholder/t.wav new file mode 100644 index 0000000..76e0948 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/t.wav differ diff --git a/sound/dialogue/animalese/placeholder/t.wav.import b/sound/dialogue/animalese/placeholder/t.wav.import new file mode 100644 index 0000000..ce35ba2 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/t.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dr0dp5v8fdb8r" +path="res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/t.wav" +dest_files=["res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/u.wav b/sound/dialogue/animalese/placeholder/u.wav new file mode 100644 index 0000000..81ebbe5 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/u.wav differ diff --git a/sound/dialogue/animalese/placeholder/u.wav.import b/sound/dialogue/animalese/placeholder/u.wav.import new file mode 100644 index 0000000..8ccd096 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/u.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bln1bkdu20muo" +path="res://.godot/imported/u.wav-06e44c423f3d1cd6cdd34b54270211bb.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/u.wav" +dest_files=["res://.godot/imported/u.wav-06e44c423f3d1cd6cdd34b54270211bb.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/v.wav b/sound/dialogue/animalese/placeholder/v.wav new file mode 100644 index 0000000..d5c0d12 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/v.wav differ diff --git a/sound/dialogue/animalese/placeholder/v.wav.import b/sound/dialogue/animalese/placeholder/v.wav.import new file mode 100644 index 0000000..ff876a8 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/v.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://y3g1s3pw3bn0" +path="res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/v.wav" +dest_files=["res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/w.wav b/sound/dialogue/animalese/placeholder/w.wav new file mode 100644 index 0000000..5122f9c Binary files /dev/null and b/sound/dialogue/animalese/placeholder/w.wav differ diff --git a/sound/dialogue/animalese/placeholder/w.wav.import b/sound/dialogue/animalese/placeholder/w.wav.import new file mode 100644 index 0000000..36f4e0f --- /dev/null +++ b/sound/dialogue/animalese/placeholder/w.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://5ml2iuf8cpgt" +path="res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/w.wav" +dest_files=["res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/x.wav b/sound/dialogue/animalese/placeholder/x.wav new file mode 100644 index 0000000..e00f085 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/x.wav differ diff --git a/sound/dialogue/animalese/placeholder/x.wav.import b/sound/dialogue/animalese/placeholder/x.wav.import new file mode 100644 index 0000000..c91ffb4 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/x.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b5al0m4de1m5x" +path="res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/x.wav" +dest_files=["res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/y.wav b/sound/dialogue/animalese/placeholder/y.wav new file mode 100644 index 0000000..07b45ef Binary files /dev/null and b/sound/dialogue/animalese/placeholder/y.wav differ diff --git a/sound/dialogue/animalese/placeholder/y.wav.import b/sound/dialogue/animalese/placeholder/y.wav.import new file mode 100644 index 0000000..2c7d8d3 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/y.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c6hust77yamoe" +path="res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/y.wav" +dest_files=["res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/animalese/placeholder/z.wav b/sound/dialogue/animalese/placeholder/z.wav new file mode 100644 index 0000000..8f94654 Binary files /dev/null and b/sound/dialogue/animalese/placeholder/z.wav differ diff --git a/sound/dialogue/animalese/placeholder/z.wav.import b/sound/dialogue/animalese/placeholder/z.wav.import new file mode 100644 index 0000000..12b1b54 --- /dev/null +++ b/sound/dialogue/animalese/placeholder/z.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://djvv63b0jlll" +path="res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample" + +[deps] + +source_file="res://sound/dialogue/animalese/placeholder/z.wav" +dest_files=["res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/calls/bark.wav b/sound/dialogue/calls/bark.wav new file mode 100644 index 0000000..2629fe7 Binary files /dev/null and b/sound/dialogue/calls/bark.wav differ diff --git a/sound/dialogue/calls/bark.wav.import b/sound/dialogue/calls/bark.wav.import new file mode 100644 index 0000000..702904d --- /dev/null +++ b/sound/dialogue/calls/bark.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c8x46srpghnil" +path="res://.godot/imported/bark.wav-9da6fd5d720375f4a64b26e364fcd74c.sample" + +[deps] + +source_file="res://sound/dialogue/calls/bark.wav" +dest_files=["res://.godot/imported/bark.wav-9da6fd5d720375f4a64b26e364fcd74c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/sound/dialogue/calls/meow.wav b/sound/dialogue/calls/meow.wav new file mode 100644 index 0000000..662b416 Binary files /dev/null and b/sound/dialogue/calls/meow.wav differ diff --git a/sound/dialogue/calls/meow.wav.import b/sound/dialogue/calls/meow.wav.import new file mode 100644 index 0000000..058b09a --- /dev/null +++ b/sound/dialogue/calls/meow.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://y86nskf7ewrg" +path="res://.godot/imported/meow.wav-eb7bd4377946745ae1566c72ca026fae.sample" + +[deps] + +source_file="res://sound/dialogue/calls/meow.wav" +dest_files=["res://.godot/imported/meow.wav-eb7bd4377946745ae1566c72ca026fae.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/tests/android_readiness_validation.gd b/tests/android_readiness_validation.gd new file mode 100644 index 0000000..b383202 --- /dev/null +++ b/tests/android_readiness_validation.gd @@ -0,0 +1,109 @@ +extends SceneTree + +const GameUIType = preload("res://ui/game_ui.gd") +const PlayerType = preload("res://player/player.gd") + + +func _init() -> void: + assert( + ProjectSettings.get_setting("display/window/handheld/orientation", -1) == 0 + ) + assert( + ProjectSettings.get_setting( + "input_devices/pointing/emulate_mouse_from_touch", false + ) + ) + assert(_has_joypad_motion(&"move_forward", JOY_AXIS_LEFT_Y, -1.0)) + assert(_has_joypad_motion(&"move_backward", JOY_AXIS_LEFT_Y, 1.0)) + assert(_has_joypad_motion(&"move_left", JOY_AXIS_LEFT_X, -1.0)) + assert(_has_joypad_motion(&"move_right", JOY_AXIS_LEFT_X, 1.0)) + assert(_has_joypad_button(&"jump", JOY_BUTTON_A)) + assert(_has_joypad_button(&"ui_accept", JOY_BUTTON_A)) + assert(_has_joypad_button(&"ui_cancel", JOY_BUTTON_B)) + assert(_has_joypad_button(&"interact", JOY_BUTTON_Y)) + assert(_has_joypad_button(&"fish_primary", JOY_BUTTON_RIGHT_SHOULDER)) + assert(not _has_joypad_motion( + &"fish_primary", JOY_AXIS_TRIGGER_LEFT, 1.0 + )) + assert(not _has_joypad_motion( + &"fish_primary", JOY_AXIS_TRIGGER_RIGHT, 1.0 + )) + assert(_has_joypad_button(&"sprint", JOY_BUTTON_LEFT_STICK)) + assert(not _has_joypad_button( + &"camera_zoom_out", JOY_BUTTON_LEFT_SHOULDER + )) + assert(not _has_joypad_button( + &"camera_zoom_in", JOY_BUTTON_RIGHT_SHOULDER + )) + assert(_has_joypad_button(&"hotbar_previous", JOY_BUTTON_DPAD_LEFT)) + assert(_has_joypad_button(&"hotbar_next", JOY_BUTTON_DPAD_RIGHT)) + assert(_has_joypad_button(&"open_backpack", JOY_BUTTON_X)) + assert(_has_joypad_button(&"open_system_menu", JOY_BUTTON_START)) + assert(_has_joypad_button(&"open_emotes", JOY_BUTTON_DPAD_UP)) + assert(_has_joypad_button(&"open_quick_actions", JOY_BUTTON_DPAD_DOWN)) + assert(_has_joypad_button(&"open_chat", JOY_BUTTON_BACK)) + assert(_has_joypad_button(&"focus_gameplay", JOY_BUTTON_LEFT_SHOULDER)) + assert( + GameUIType.VIRTUAL_MOUSE_TRIGGER_AXIS == JOY_AXIS_TRIGGER_RIGHT + ) + assert( + GameUIType.VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS + == JOY_AXIS_TRIGGER_LEFT + ) + assert( + GameUIType.VIRTUAL_MOUSE_SECONDARY_CLICK_AXIS + == JOY_AXIS_TRIGGER_LEFT + ) + assert(PlayerType.CONTROLLER_ZOOM_TRIGGER_AXIS == JOY_AXIS_TRIGGER_LEFT) + assert(is_zero_approx( + GameUIType.normalized_trigger_strength(-1.0, -1.0) + )) + assert(is_equal_approx( + GameUIType.normalized_trigger_strength(1.0, -1.0), + 1.0, + )) + assert(is_equal_approx( + GameUIType.normalized_trigger_strength(-1.0, 0.0), + 1.0, + )) + assert(is_equal_approx( + GameUIType.normalized_trigger_strength(-1.0, 1.0), + 1.0, + )) + assert(is_equal_approx( + GameUIType.directional_trigger_strength(-1.0, 0.0, -1.0), + 1.0, + )) + assert(is_zero_approx( + GameUIType.directional_trigger_strength(1.0, 0.0, -1.0) + )) + assert(is_equal_approx( + GameUIType.directional_trigger_strength(1.0, 0.0, 1.0), + 1.0, + )) + print("android readiness validation passed") + quit() + + +func _has_joypad_button(action: StringName, button_index: JoyButton) -> bool: + for event: InputEvent in InputMap.action_get_events(action): + var button_event: InputEventJoypadButton = event as InputEventJoypadButton + if button_event != null and button_event.button_index == button_index: + return true + return false + + +func _has_joypad_motion( + action: StringName, + axis: JoyAxis, + direction: float, +) -> bool: + for event: InputEvent in InputMap.action_get_events(action): + var motion_event: InputEventJoypadMotion = event as InputEventJoypadMotion + if ( + motion_event != null + and motion_event.axis == axis + and is_equal_approx(motion_event.axis_value, direction) + ): + return true + return false diff --git a/tests/android_readiness_validation.gd.uid b/tests/android_readiness_validation.gd.uid new file mode 100644 index 0000000..f499a0b --- /dev/null +++ b/tests/android_readiness_validation.gd.uid @@ -0,0 +1 @@ +uid://cbqyx4mntux7l diff --git a/tests/art_tools_validation.gd b/tests/art_tools_validation.gd index ab04d4a..14e19e7 100644 --- a/tests/art_tools_validation.gd +++ b/tests/art_tools_validation.gd @@ -27,6 +27,55 @@ func _run() -> void: await physics_frame var player := main.get("_player") as Player + var sprint_dust := player.get_node("%SprintDust") as SprintDustTrail + assert(sprint_dust != null) + assert(sprint_dust.get_active_puff_count() == 0) + sprint_dust.update_trail( + 0.016, + Vector3.ZERO, + Vector3(4.5, 0.0, 0.0), + true, + ) + assert(sprint_dust.get_active_puff_count() == 0) + sprint_dust.update_trail( + 0.016, + Vector3(0.5, 0.0, 0.0), + Vector3(4.5, 0.0, 0.0), + true, + ) + assert(sprint_dust.get_active_puff_count() == 1) + var planted_puff_position: Vector3 = ( + sprint_dust.get_first_active_puff_position() + ) + sprint_dust.update_trail( + 0.08, + Vector3(0.75, 0.0, 0.0), + Vector3(4.5, 0.0, 0.0), + true, + ) + assert( + sprint_dust.get_first_active_puff_position() + == planted_puff_position + ) + sprint_dust.update_trail( + 0.08, + Vector3(0.75, 0.0, 0.0), + Vector3.ZERO, + false, + ) + assert(sprint_dust.get_active_puff_count() == 1) + sprint_dust.clear_trail() + assert(sprint_dust.get_active_puff_count() == 0) + sprint_dust.emit_landing_burst(Vector3.ZERO, Vector3.FORWARD) + assert(sprint_dust.get_active_puff_count() == 5) + sprint_dust.update_trail( + 0.08, + Vector3.ZERO, + Vector3.ZERO, + false, + ) + assert(sprint_dust.get_active_puff_count() == 5) + sprint_dust.clear_trail() var service := main.get_node( "%NetworkSurfaceDrawingService" ) as NetworkSurfaceDrawingService @@ -37,17 +86,33 @@ func _run() -> void: var chat_ui := game_ui.get_node("%ChatUI") as ChatUI assert(player != null and service != null and toolbar != null) assert(chat_ui != null) + assert(bool(game_ui.call("_can_start_virtual_mouse"))) + game_ui.call("_begin_virtual_mouse", 0) + var virtual_cursor := game_ui.get_node( + "%ControllerVirtualCursor" + ) as ControllerVirtualCursor + assert(virtual_cursor.visible) + assert(not player.is_camera_input_enabled()) + game_ui.call("_end_virtual_mouse") + assert(not virtual_cursor.visible) + assert(player.is_camera_input_enabled()) var settings_panel := game_ui.get( "_pause_settings_panel" ) as SettingsPanel var chat_dock_button := settings_panel.get_node("%ChatDock") as BubbleButton + var chat_mode_button := settings_panel.get_node("%ChatMode") as BubbleButton var paint_dock_button := settings_panel.get_node("%PaintDock") as BubbleButton assert(chat_dock_button.neutral_size.x == chat_dock_button.neutral_size.y) + assert(chat_mode_button.neutral_size.x == chat_mode_button.neutral_size.y) assert(paint_dock_button.neutral_size.x == paint_dock_button.neutral_size.y) assert( chat_dock_button.compact_minimum_size.x == chat_dock_button.compact_minimum_size.y ) + assert( + chat_mode_button.compact_minimum_size.x + == chat_mode_button.compact_minimum_size.y + ) assert( paint_dock_button.compact_minimum_size.x == paint_dock_button.compact_minimum_size.y @@ -60,35 +125,160 @@ func _run() -> void: assert(settings_manager != null) var changed_docks: PlayerSettings = settings_manager.current_settings.copy() changed_docks.chat_dock_right = true + changed_docks.chat_mobile_mode = true changed_docks.paint_dock_right = false assert(settings_manager.apply_settings(changed_docks)) await process_frame assert(chat_ui.is_docked_right()) + assert(chat_ui.is_mobile_mode()) assert(not toolbar.is_docked_right()) + var chat_panel := chat_ui.get_node("ChatPanel") as PanelContainer + var chat_height_button := chat_ui.get_node("ChatHeightButton") as Button + var chat_status := chat_ui.find_child( + "ChatStatus", true, false + ) as Label + assert(chat_status != null) + assert( + chat_status.autowrap_mode + == TextServer.AUTOWRAP_WORD_SMART + ) + assert(is_zero_approx(chat_panel.position.y)) + assert(is_equal_approx(chat_panel.size.x, ChatUI.MOBILE_COMPACT_WIDTH)) + assert(not chat_height_button.visible) var reloaded_settings := PlayerSettingsManager.new() root.add_child(reloaded_settings) assert(reloaded_settings.load_settings()) assert(reloaded_settings.current_settings.chat_dock_right) + assert(reloaded_settings.current_settings.chat_mobile_mode) assert(not reloaded_settings.current_settings.paint_dock_right) reloaded_settings.queue_free() var default_docks: PlayerSettings = settings_manager.current_settings.copy() default_docks.chat_dock_right = false + default_docks.chat_mobile_mode = false default_docks.paint_dock_right = true assert(settings_manager.apply_settings(default_docks)) await process_frame + chat_ui.call( + "_set_status", + ( + "Unknown command: /this-command-name-is-intentionally-long-" + + "enough-to-require-smart-character-wrapping" + ), + ) + await process_frame + assert(is_equal_approx(chat_panel.size.x, ChatUI.PANEL_WIDTH)) + assert(chat_status.size.x <= ChatUI.PANEL_WIDTH) + chat_ui.call("_set_status", "") assert(not chat_ui.is_docked_right()) + assert(not chat_ui.is_mobile_mode()) assert(toolbar.is_docked_right()) + assert(chat_height_button.visible) + var select_button := InputEventJoypadButton.new() + select_button.button_index = JOY_BUTTON_BACK + select_button.pressed = true + assert(bool(game_ui.call("_handle_controller_chat_controls", select_button))) + await process_frame + assert(chat_ui.is_open()) + assert(chat_panel.mouse_filter == Control.MOUSE_FILTER_STOP) + var typed_chat_entry := chat_ui.find_child( + "ChatEntry", true, false + ) as LineEdit + assert(typed_chat_entry != null) + assert(not typed_chat_entry.virtual_keyboard_enabled) + var accept_button := InputEventJoypadButton.new() + accept_button.button_index = JOY_BUTTON_A + accept_button.pressed = true + assert(bool(game_ui.call("_handle_controller_chat_controls", accept_button))) + assert(typed_chat_entry.virtual_keyboard_enabled) + await process_frame + assert(typed_chat_entry.has_focus()) + var left_bumper := InputEventJoypadButton.new() + left_bumper.button_index = JOY_BUTTON_LEFT_SHOULDER + left_bumper.pressed = true + assert(bool(game_ui.call("_handle_controller_chat_controls", left_bumper))) + assert(not chat_ui.is_open()) + assert(chat_panel.mouse_filter == Control.MOUSE_FILTER_IGNORE) + assert(not typed_chat_entry.virtual_keyboard_enabled) + assert(bool(game_ui.call("_handle_controller_chat_controls", left_bumper))) + assert(not chat_ui.is_open()) + assert(not typed_chat_entry.virtual_keyboard_enabled) + assert(bool(game_ui.call("_handle_controller_chat_controls", select_button))) + assert(not chat_ui.is_open()) + assert(chat_ui.is_collapsed()) + assert(bool(game_ui.call("_handle_controller_chat_controls", select_button))) + assert(chat_ui.is_open()) + chat_ui.refocus_gameplay() + var quick_menu := game_ui.get_node( + "%QuickRadialMenu" + ) as QuickRadialMenu + var emote_menu := game_ui.get_node( + "%EmoteRadialMenu" + ) as EmoteRadialMenu + assert(quick_menu != null and emote_menu != null) + assert(QuickRadialMenu.ACTIONS.size() == QuickRadialMenu.SECTOR_COUNT) + assert(QuickRadialMenu.ACTIONS.has(&"freecam")) + assert(QuickRadialMenu.ACTIONS.has(&"hud")) + game_ui.call("_on_quick_action_selected", &"freecam") + assert(player.is_free_camera_active()) + assert(player.get_active_gameplay_camera().name == &"FreeCamera") + game_ui.call("_on_quick_action_selected", &"freecam") + assert(not player.is_free_camera_active()) + game_ui.call("_on_quick_action_selected", &"hud") + await process_frame + assert(game_ui.is_gameplay_hud_hidden()) + assert(chat_ui.is_hud_hidden()) + assert(not ( + game_ui.get_node("%GameplayTransientHUD") as Control + ).visible) + assert(not ( + game_ui.get_node("%ExperiencePresentation") as Control + ).visible) + quick_menu.open_menu(true) + assert(quick_menu.visible and quick_menu.is_open()) + quick_menu.close_menu() + emote_menu.open_menu(true) + assert(emote_menu.visible and emote_menu.is_open()) + emote_menu.close_menu() + chat_ui.open_chat() + await process_frame + assert(chat_ui.is_open() and chat_panel.visible) + chat_ui.refocus_gameplay() + assert(chat_ui.is_hud_hidden()) + var cancel_button := InputEventJoypadButton.new() + cancel_button.button_index = JOY_BUTTON_B + cancel_button.pressed = true + assert(not bool(main.call("_is_pause_open_request", cancel_button))) + var start_button := InputEventJoypadButton.new() + start_button.button_index = JOY_BUTTON_START + start_button.pressed = true + assert(bool(main.call("_is_pause_open_request", start_button))) + var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu + player_menu.open_section(PlayerMenu.Section.PROFILE) + for _frame: int in 12: + await process_frame + assert(game_ui.is_gameplay_hud_hidden()) + assert(player_menu.visible) + assert(root.gui_get_focus_owner() is not LineEdit) + player_menu.close_menu(PlayerMenu.CloseReason.SESSION_END) + await process_frame + game_ui.call("_on_quick_action_selected", &"hud") + await process_frame + assert(not game_ui.is_gameplay_hud_hidden()) + assert(not chat_ui.is_hud_hidden()) assert(not service.can_activate()) assert(not service.is_active() and not toolbar.visible) assert(player.bag.add_item(ArtShopStock.ART_KIT_ITEM_ID, 1)) - assert(service.can_activate()) - + player.hotbar.select_slot(1) + assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID)) + assert(not service.can_activate()) var paint_key := InputEventKey.new() paint_key.physical_keycode = KEY_P paint_key.pressed = true - assert(service.handle_input(paint_key, true)) + assert(not service.handle_input(paint_key, true)) + assert(not service.is_active()) + player.hotbar.select_slot(0) await process_frame - assert(service.is_active() and service.is_placement_mode()) + assert(service.is_active() and not service.is_placement_mode()) assert(toolbar.visible) var ui_root := game_ui.get_node("%UIRoot") as Control assert(toolbar.get_parent() == ui_root) @@ -103,6 +293,8 @@ func _run() -> void: assert(toolbar.get_global_rect().end.y <= 720.0) var top_panel := toolbar.get_node("%TopPanel") as PanelContainer var color_panel := toolbar.get_node("%ColorPanel") as PanelContainer + assert(is_equal_approx(toolbar.size.x, SurfaceDrawingToolbar.TOOLBAR_WIDTH)) + assert(is_equal_approx(top_panel.size.x, SurfaceDrawingToolbar.TOOLBAR_WIDTH)) assert(top_panel.position.is_equal_approx(Vector2.ZERO)) assert(color_panel.position.x > 0.0) assert(is_equal_approx(color_panel.get_rect().end.x, top_panel.get_rect().end.x)) @@ -127,7 +319,6 @@ func _run() -> void: var hide_button := toolbar.get_node("%HideGuideButton") as Button var restore_button := toolbar.get_node("%RestoreGuideButton") as Button var finalize_button := toolbar.get_node("%FinalizeGuideButton") as Button - var close_button := toolbar.get_node("%CloseButton") as Button assert( mode_button != null and eraser_button != null @@ -135,8 +326,44 @@ func _run() -> void: and hide_button != null and restore_button != null and finalize_button != null - and close_button != null ) + assert(toolbar.get_node_or_null("%CloseButton") == null) + var expected_toolbar_icons: Dictionary[Button, String] = { + mode_button: "/art/art_kit_marker.png", + eraser_button: "/art/art_kit_eraser.png", + hide_button: "/art/art_kit_grid_hidden_light.png", + restore_button: "/art/art_kit_grid_restore_light.png", + finalize_button: "/art/art_kit_grid_finish_light.png", + } + for icon_button: Button in expected_toolbar_icons: + assert(icon_button.text.is_empty()) + assert(icon_button.icon != null) + assert( + icon_button.icon.resource_path.ends_with( + expected_toolbar_icons[icon_button] + ) + ) + for icon_button: Button in [ + eraser_button, + undo_button, + hide_button, + restore_button, + finalize_button, + ]: + assert(icon_button.custom_minimum_size == Vector2(48, 44)) + assert(icon_button.get_theme_constant("icon_max_width") == 40) + var icon_style := ( + icon_button.get_theme_stylebox("normal") as StyleBoxFlat + ) + assert(is_equal_approx(icon_style.content_margin_left, 4.0)) + assert(is_equal_approx(icon_style.content_margin_top, 2.0)) + assert(mode_button.custom_minimum_size == Vector2(48, 48)) + assert(mode_button.get_theme_constant("icon_max_width") == 40) + assert(mode_button.get_index() > finalize_button.get_index()) + assert(hide_button.button_group != null) + assert(hide_button.button_group == restore_button.button_group) + assert(hide_button.button_group == finalize_button.button_group) + assert(hide_button.button_group.allow_unpress) assert(brush_option.item_count == 4) assert(grid_option.item_count == 4) assert(not brush_option.get_popup().is_item_disabled(0)) @@ -162,9 +389,25 @@ func _run() -> void: assert(service.get_color_id() == &"ocean_teal") assert(service.get_brush_size() == 4) assert(service.get_grid_size() == 128) + var marker_mode_material := mode_button.material as ShaderMaterial + assert(marker_mode_material != null) + assert( + marker_mode_material.shader.resource_path.ends_with( + "/ui/art_kit_marker_icon.gdshader" + ) + ) + assert( + marker_mode_material.get_shader_parameter("marker_color") + == SurfaceDrawingPalette.get_color(&"ocean_teal") + ) mode_button.pressed.emit() - assert(not service.is_placement_mode()) + assert(service.is_placement_mode()) + assert( + mode_button.icon.resource_path.ends_with( + "/art/art_kit_grid_light.png" + ) + ) eraser_button.pressed.emit() assert(service.is_eraser_mode()) assert(eraser_button.button_pressed) @@ -179,55 +422,118 @@ func _run() -> void: world_click.button_index = MOUSE_BUTTON_LEFT world_click.pressed = true assert(service.handle_input(world_click, true)) + assert( + service.get_armed_guide_action() + == NetworkSurfaceDrawingService.GuideAction.HIDE + ) + assert(service.is_placement_mode()) + assert(not service.is_eraser_mode()) + assert(hide_button.button_pressed) + hide_button.pressed.emit() assert( service.get_armed_guide_action() == NetworkSurfaceDrawingService.GuideAction.NONE ) - assert(not service.is_placement_mode()) - assert(service.is_eraser_mode()) + assert(not service.is_placement_mode() and service.is_eraser_mode()) restore_button.pressed.emit() assert( service.get_armed_guide_action() == NetworkSurfaceDrawingService.GuideAction.RESTORE ) - restore_button.pressed.emit() - assert( - service.get_armed_guide_action() - == NetworkSurfaceDrawingService.GuideAction.NONE - ) - assert(not service.is_placement_mode() and service.is_eraser_mode()) finalize_button.pressed.emit() assert( service.get_armed_guide_action() == NetworkSurfaceDrawingService.GuideAction.FINALIZE ) + assert(not restore_button.button_pressed) + assert(finalize_button.button_pressed) assert(service.handle_input(world_click, true)) + assert( + service.get_armed_guide_action() + == NetworkSurfaceDrawingService.GuideAction.FINALIZE + ) + finalize_button.pressed.emit() + assert( + service.get_armed_guide_action() + == NetworkSurfaceDrawingService.GuideAction.NONE + ) assert(not service.is_placement_mode() and service.is_eraser_mode()) (color_buttons[&"ocean_teal"] as Button).pressed.emit() assert(not service.is_eraser_mode()) undo_button.pressed.emit() assert(service.is_active()) - close_button.pressed.emit() - assert(not service.is_active() and not toolbar.visible) - - assert(service.handle_input(paint_key, true)) - await process_frame + assert(not service.handle_input(paint_key, true)) assert(service.is_active() and toolbar.visible) - - assert(service.handle_input(paint_key, true)) - await process_frame - assert(not service.is_active() and not toolbar.visible) - assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID)) var fishing_spot := main.get_node("%FishingSpot") as FishingSpot - fishing_spot.art_ui_toggle_requested.emit() await process_frame + var held_art_kit_display := player.get("_held_art_kit_display") as Node3D + var held_art_kit_sprite := player.get("_held_art_kit_sprite") as Sprite3D + var pocket_animation_player := player.get( + "_character_animation_player" + ) as AnimationPlayer + var pocket_animation_name: StringName = player.get( + "_pocket_visual_animation" + ) + if ( + not held_art_kit_display.visible + and pocket_animation_player != null + and not pocket_animation_name.is_empty() + and pocket_animation_player.has_animation(pocket_animation_name) + ): + var pocket_animation: Animation = ( + pocket_animation_player.get_animation(pocket_animation_name) + ) + await create_timer(pocket_animation.length * 0.55).timeout + assert(held_art_kit_display.visible) + assert(held_art_kit_sprite.visible and held_art_kit_sprite.texture != null) + assert(held_art_kit_sprite.texture.resource_path.ends_with("/art/art_kit.png")) + assert(is_zero_approx(angle_difference( + held_art_kit_sprite.rotation.z, + -PI * 0.5, + ))) + assert(held_art_kit_sprite.flip_h) + assert(not fishing_spot.has_signal(&"art_ui_toggle_requested")) assert(service.is_active() and toolbar.visible) assert(service.get_grid_size() == 128) + assert(service.handle_input(world_click, true)) + assert(service.is_active() and toolbar.visible) + var hotbar_ui := game_ui.get_node("%Hotbar") as HotbarUI + var wheel_down := InputEventMouseButton.new() + wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN + wheel_down.pressed = true + assert(not service.handle_input(wheel_down, true)) + hotbar_ui._unhandled_input(wheel_down) + await process_frame + assert(player.hotbar.get_selected_slot() == 1) + assert(not service.is_active() and not toolbar.visible) + + var number_one := InputEventKey.new() + number_one.physical_keycode = KEY_1 + number_one.pressed = true + assert(not service.handle_input(number_one, true)) + hotbar_ui._unhandled_input(number_one) + await process_frame + assert(service.is_active() and toolbar.visible) + assert(player.hotbar.get_selected_slot() == 0) + var number_two := InputEventKey.new() + number_two.physical_keycode = KEY_2 + number_two.pressed = true + assert(not service.handle_input(number_two, true)) + hotbar_ui._unhandled_input(number_two) + await process_frame + assert(player.hotbar.get_selected_slot() == 1) + assert(not service.is_active() and not toolbar.visible) + game_ui.call("_on_quick_action_selected", &"paint") + await process_frame + assert(player.hotbar.get_selected_slot() == 0) + assert(service.is_active() and toolbar.visible) print("Art tools validation: PASS") var session := main.get_node("%NetworkSession") as NetworkSession session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() diff --git a/tests/character_scale_validation.gd b/tests/character_scale_validation.gd new file mode 100644 index 0000000..213f64f --- /dev/null +++ b/tests/character_scale_validation.gd @@ -0,0 +1,117 @@ +extends SceneTree + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var legacy_snapshot: Dictionary = ( + CharacterCustomizationCatalog.default_snapshot() + ) + legacy_snapshot.erase(CharacterCustomizationCatalog.SCALE_CATEGORY_ID) + var migrated: Dictionary = ( + CharacterCustomizationCatalog.sanitized_snapshot(legacy_snapshot) + ) + assert( + is_equal_approx( + float(migrated[CharacterCustomizationCatalog.SCALE_CATEGORY_ID]), + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ) + assert(CharacterCustomizationCatalog.validate_snapshot(migrated)) + + var small_snapshot := migrated.duplicate(true) + small_snapshot[CharacterCustomizationCatalog.SCALE_CATEGORY_ID] = ( + CharacterCustomizationCatalog.MIN_CHARACTER_SCALE + ) + assert(CharacterCustomizationCatalog.validate_snapshot(small_snapshot)) + var large_snapshot := migrated.duplicate(true) + large_snapshot[CharacterCustomizationCatalog.SCALE_CATEGORY_ID] = ( + CharacterCustomizationCatalog.MAX_CHARACTER_SCALE + ) + assert(CharacterCustomizationCatalog.validate_snapshot(large_snapshot)) + var invalid_snapshot := migrated.duplicate(true) + invalid_snapshot[CharacterCustomizationCatalog.SCALE_CATEGORY_ID] = 2.0 + assert(not CharacterCustomizationCatalog.validate_snapshot(invalid_snapshot)) + + var visuals: Node3D = PlayerVisualPresenter.instantiate_visuals() + root.add_child(visuals) + PlayerVisualPresenter.apply_appearance(visuals, large_snapshot) + assert(visuals.scale.is_equal_approx( + Vector3.ONE * CharacterCustomizationCatalog.MAX_CHARACTER_SCALE + )) + var skeleton := visuals.find_child("Skeleton3D", true, false) as Skeleton3D + assert(skeleton != null) + var body := skeleton.get_node_or_null("body_main") as MeshInstance3D + assert(body != null) + var body_material := body.material_override as StandardMaterial3D + assert(body_material != null) + assert( + body_material.texture_filter + == BaseMaterial3D.TEXTURE_FILTER_NEAREST + ) + assert(body_material.shading_mode == BaseMaterial3D.SHADING_MODE_UNSHADED) + assert(is_zero_approx(body_material.metallic)) + assert(is_zero_approx(body_material.metallic_specular)) + assert(is_equal_approx(body_material.roughness, 1.0)) + assert(body_material.diffuse_mode == BaseMaterial3D.DIFFUSE_LAMBERT) + assert(not body_material.anisotropy_enabled) + assert(not body_material.clearcoat_enabled) + assert(not body_material.rim_enabled) + + var signed_small := {"appearance": small_snapshot} + var signed_large := {"appearance": large_snapshot} + assert( + NetworkProfileProtocol.signature_fields(signed_small) + != NetworkProfileProtocol.signature_fields(signed_large) + ) + + assert( + ProfilePreview.calculate_pixelated_grid_size( + Vector2(260.0, 330.0), + Vector2(1280.0, 720.0), + 3, + ) == Vector2i(130, 165) + ) + assert( + ProfilePreview.calculate_pixelated_grid_size( + Vector2(260.0, 330.0), + Vector2(640.0, 480.0), + 3, + ) == Vector2i(65, 83) + ) + + var profile_page := ProfilePage.new() + root.add_child(profile_page) + await process_frame + profile_page.call( + "_select_category", + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + ) + await process_frame + var slider := profile_page.find_child( + "CharacterScaleSlider", true, false + ) as HSlider + assert(slider != null) + assert(is_equal_approx( + float(slider.min_value), + CharacterCustomizationCatalog.MIN_CHARACTER_SCALE, + )) + assert(is_equal_approx( + float(slider.max_value), + CharacterCustomizationCatalog.MAX_CHARACTER_SCALE, + )) + slider.value = 1.15 + await process_frame + var draft: Dictionary = profile_page.get("_draft_appearance") + assert(is_equal_approx( + float(draft[CharacterCustomizationCatalog.SCALE_CATEGORY_ID]), + 1.15, + )) + + print("Character scale validation: PASS") + profile_page.queue_free() + visuals.queue_free() + await process_frame + quit() diff --git a/tests/character_scale_validation.gd.uid b/tests/character_scale_validation.gd.uid new file mode 100644 index 0000000..c74c5d0 --- /dev/null +++ b/tests/character_scale_validation.gd.uid @@ -0,0 +1 @@ +uid://d0w8gv1i4paii diff --git a/tests/controller_focus_presentation_validation.gd b/tests/controller_focus_presentation_validation.gd new file mode 100644 index 0000000..f0d0355 --- /dev/null +++ b/tests/controller_focus_presentation_validation.gd @@ -0,0 +1,84 @@ +extends SceneTree + +const FocusPresentationType = preload( + "res://ui/controller_focus_presentation.gd" +) + +var _failures: Array[String] = [] + + +func _initialize() -> void: + _run.call_deferred() + + +func _run() -> void: + var stage := Control.new() + stage.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + root.add_child(stage) + var presentation := FocusPresentationType.new() + stage.add_child(presentation) + var standard_button := Button.new() + standard_button.text = "standard" + standard_button.custom_minimum_size = Vector2(120.0, 60.0) + stage.add_child(standard_button) + var authored_selector := Button.new() + authored_selector.text = "authored selector" + authored_selector.position = Vector2(140.0, 0.0) + authored_selector.custom_minimum_size = Vector2(160.0, 60.0) + authored_selector.set_meta(&"controller_focus_inversion_disabled", true) + stage.add_child(authored_selector) + await process_frame + + var controller_event := InputEventJoypadButton.new() + controller_event.button_index = JOY_BUTTON_A + controller_event.pressed = true + presentation._input(controller_event) + standard_button.grab_focus() + await process_frame + _expect( + standard_button.material != null, + "ordinary controller focus receives inversion", + ) + var pointer_motion := InputEventMouseMotion.new() + pointer_motion.position = Vector2(4.0, 4.0) + presentation._input(pointer_motion) + await process_frame + _expect( + standard_button.material == null, + "mouse motion clears controller focus presentation", + ) + presentation._input(controller_event) + + authored_selector.grab_focus() + await process_frame + _expect( + standard_button.material == null, + "inversion clears when focus moves", + ) + _expect( + authored_selector.material == null, + "authored selector backgrounds opt out of inversion", + ) + + standard_button.grab_focus() + await process_frame + standard_button.focus_mode = Control.FOCUS_NONE + await process_frame + _expect( + standard_button.material == null, + "inversion clears when a focused control is defocused", + ) + + stage.queue_free() + if _failures.is_empty(): + print("Controller focus presentation validation: PASS") + quit(0) + return + for failure: String in _failures: + push_error(failure) + quit(1) + + +func _expect(condition: bool, message: String) -> void: + if not condition: + _failures.append(message) diff --git a/tests/controller_focus_presentation_validation.gd.uid b/tests/controller_focus_presentation_validation.gd.uid new file mode 100644 index 0000000..0cacf4a --- /dev/null +++ b/tests/controller_focus_presentation_validation.gd.uid @@ -0,0 +1 @@ +uid://bjay8ki3albk6 diff --git a/tests/controller_focus_recovery_validation.gd b/tests/controller_focus_recovery_validation.gd new file mode 100644 index 0000000..f598511 --- /dev/null +++ b/tests/controller_focus_recovery_validation.gd @@ -0,0 +1,184 @@ +extends SceneTree + +const ControllerFocusRecoveryType = preload( + "res://ui/controller_focus_recovery.gd" +) + +var _failures: Array[String] = [] + + +func _initialize() -> void: + _run.call_deferred() + + +func _run() -> void: + var stage := Control.new() + stage.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + root.add_child(stage) + var recovery := ControllerFocusRecoveryType.new() + stage.add_child(recovery) + var option_list := VBoxContainer.new() + option_list.position = Vector2(100.0, 100.0) + stage.add_child(option_list) + var original := _make_button("cute", "cute (show variants)") + option_list.add_child(original) + await process_frame + original.grab_focus() + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "programmatic focus stays pending outside navigation mode", + ) + var keyboard_navigation := InputEventKey.new() + keyboard_navigation.keycode = KEY_DOWN + keyboard_navigation.pressed = true + recovery._input(keyboard_navigation) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == original, + "keyboard navigation restores pending initial focus", + ) + var menu_shortcut := InputEventKey.new() + menu_shortcut.physical_keycode = KEY_TAB + menu_shortcut.pressed = true + recovery._input(menu_shortcut) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "the menu shortcut does not leave a pseudo-focused option", + ) + + var controller_event := InputEventJoypadButton.new() + controller_event.button_index = JOY_BUTTON_A + controller_event.pressed = true + recovery._input(controller_event) + await process_frame + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == original, + "controller navigation restores pending initial focus", + ) + + option_list.remove_child(original) + original.queue_free() + var replacement := _make_button("cute", "cute (hide variants)") + option_list.add_child(replacement) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == replacement, + "focus follows a rebuilt semantic option", + ) + + var explicit_target := _make_button("explicit", "explicit") + option_list.add_child(explicit_target) + option_list.remove_child(replacement) + replacement.queue_free() + explicit_target.grab_focus() + await process_frame + _expect( + root.gui_get_focus_owner() == explicit_target, + "explicit focus changes take precedence over recovery", + ) + var pointer_motion := InputEventMouseMotion.new() + pointer_motion.position = Vector2(4.0, 4.0) + recovery._input(pointer_motion) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "mouse motion releases controller focus", + ) + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "mouse motion prevents automatic focus recovery", + ) + recovery._input(controller_event) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == explicit_target, + "controller navigation can resume from pointer mode", + ) + var pointer_press := InputEventMouseButton.new() + pointer_press.button_index = MOUSE_BUTTON_LEFT + pointer_press.pressed = true + recovery._input(pointer_press) + await process_frame + _expect( + root.gui_get_focus_owner() == explicit_target, + "mouse-down preserves focus through button activation", + ) + var pointer_release := InputEventMouseButton.new() + pointer_release.button_index = MOUSE_BUTTON_LEFT + pointer_release.pressed = false + recovery._input(pointer_release) + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "mouse-up clears the completed button focus", + ) + recovery._input(controller_event) + await process_frame + await process_frame + + var outside_button := _make_button("outside", "outside") + stage.add_child(outside_button) + option_list.hide() + root.gui_release_focus() + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "focus recovery never escapes a hidden menu scope", + ) + option_list.show() + explicit_target.grab_focus() + await process_frame + var leave_world_ui_event := InputEventJoypadButton.new() + leave_world_ui_event.button_index = JOY_BUTTON_LEFT_SHOULDER + leave_world_ui_event.pressed = true + recovery._input(leave_world_ui_event) + root.gui_release_focus() + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "LB intentionally leaving world UI is never recovered", + ) + var accessibility_only := Control.new() + accessibility_only.focus_mode = Control.FOCUS_ACCESSIBILITY + stage.add_child(accessibility_only) + await process_frame + _expect( + not bool(recovery.call("_is_focusable", accessibility_only)), + "accessibility-only controls are not recovery targets", + ) + + stage.queue_free() + if _failures.is_empty(): + print("Controller focus recovery validation: PASS") + quit(0) + return + for failure: String in _failures: + push_error(failure) + quit(1) + + +func _make_button(text: String, tooltip: String) -> Button: + var button := Button.new() + button.text = text + button.tooltip_text = tooltip + button.custom_minimum_size = Vector2(120.0, 48.0) + return button + + +func _expect(condition: bool, message: String) -> void: + if not condition: + _failures.append(message) diff --git a/tests/controller_focus_recovery_validation.gd.uid b/tests/controller_focus_recovery_validation.gd.uid new file mode 100644 index 0000000..7bcb1a2 --- /dev/null +++ b/tests/controller_focus_recovery_validation.gd.uid @@ -0,0 +1 @@ +uid://cqk75fxykqrhs diff --git a/tests/controller_mapping_validation.gd b/tests/controller_mapping_validation.gd new file mode 100644 index 0000000..16c9ad0 --- /dev/null +++ b/tests/controller_mapping_validation.gd @@ -0,0 +1,388 @@ +extends SceneTree + +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const ControllerMappingPanelType = preload( + "res://ui/controller_mapping_panel.gd" +) +const GameUIType = preload("res://ui/game_ui.gd") + + +func _init() -> void: + call_deferred("_run") + + +func _run() -> void: + var manager := ControllerMappingManagerType.new() + root.add_child(manager) + await process_frame + var failure: String = _validate_manager(manager) + if failure.is_empty(): + failure = _validate_portmaster_launcher() + if failure.is_empty(): + failure = _validate_auto_map(manager) + if failure.is_empty(): + failure = _validate_virtual_mouse_bounds() + if failure.is_empty(): + failure = _validate_controller_scroll_target() + if failure.is_empty(): + print("controller mapping validation passed") + quit(0) + return + push_error(failure) + quit(1) + + +func _validate_manager(manager: ControllerMappingManagerType) -> String: + var muos_mapping: String = ( + ControllerMappingManagerType.build_muos_controller_mapping( + "19000000010000000100000000010000", + "muOS-Keys", + ) + ) + for expected_binding: String in [ + "a:b0", + "b:b1", + "x:b3", + "y:b2", + "leftshoulder:b4", + "rightshoulder:b5", + "lefttrigger:b10", + "righttrigger:b11", + "back:b6", + "start:b7", + "guide:b8", + "leftstick:b9", + "rightstick:b12", + "leftx:a0", + "righty:a3", + ]: + if expected_binding not in muos_mapping: + return "muOS compatibility mapping omitted " + expected_binding + if not muos_mapping.begins_with( + "19000000010000000100000000010000,muOS-Keys," + ): + return "muOS compatibility mapping does not use the runtime guid" + if not ControllerMappingManagerType.is_muos_controller_name( + "muOS-Keys" + ): + return "muOS controller name was not recognized" + if ControllerMappingManagerType.is_muos_controller_name( + "ordinary controller" + ): + return "muOS mapping would affect unrelated controllers" + if not ControllerMappingManagerType.should_install_muos_compatibility_mapping( + "muOS-Keys", + false, + ): + return "unknown legacy muOS controller did not receive fallback mapping" + if ControllerMappingManagerType.should_install_muos_compatibility_mapping( + "muOS-Keys", + true, + ): + return "recognized muOS controller would be replaced at runtime" + if ControllerMappingManagerType.should_install_muos_compatibility_mapping( + "ordinary controller", + false, + ): + return "muOS fallback would affect an unrelated unknown controller" + var defaults: Dictionary = manager.get_active_bindings() + if defaults.size() != ControllerMappingManagerType.ROLE_ORDER.size(): + return "default mapping covers %d of %d controller roles: %s" % [ + defaults.size(), + ControllerMappingManagerType.ROLE_ORDER.size(), + str(defaults.keys()), + ] + var trigger_button := InputEventJoypadButton.new() + trigger_button.device = manager.get_active_device_id() + trigger_button.button_index = JOY_BUTTON_MISC1 + trigger_button.pressed = true + var trigger_binding: Dictionary = manager.binding_from_event( + ControllerMappingManagerType.ROLE_POINTER_MODIFIER, + trigger_button, + ) + if str(trigger_binding.get("kind", "")) != "button": + return "trigger role did not accept a button-backed handheld trigger" + var trigger_motion := InputEventJoypadMotion.new() + trigger_motion.device = manager.get_active_device_id() + trigger_motion.axis = JOY_AXIS_TRIGGER_RIGHT + trigger_motion.axis_value = 1.0 + trigger_binding = manager.binding_from_event( + ControllerMappingManagerType.ROLE_POINTER_MODIFIER, + trigger_motion, + ) + if str(trigger_binding.get("kind", "")) != "axis": + return "trigger role did not accept an axis-backed controller trigger" + var stick_button := manager.binding_from_event( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X, + trigger_button, + ) + if not stick_button.is_empty(): + return "stick axis role accepted a button" + var keyboard_events_before: int = _keyboard_event_count(&"jump") + var custom: Dictionary = defaults.duplicate(true) + custom[str(ControllerMappingManagerType.ROLE_A)] = { + "kind": "button", + "button": int(JOY_BUTTON_X), + } + custom[str(ControllerMappingManagerType.ROLE_POINTER_MODIFIER)] = ( + trigger_binding + ) + if not manager.replace_active_bindings(custom): + return "valid custom mapping could not be saved" + if not manager.has_custom_mapping(): + return "saved custom mapping did not become active" + if _keyboard_event_count(&"jump") != keyboard_events_before: + return "controller remapping changed keyboard bindings" + var active_button := InputEventJoypadButton.new() + active_button.device = manager.get_active_device_id() + active_button.button_index = JOY_BUTTON_X + active_button.pressed = true + if not manager.event_matches_role( + active_button, + ControllerMappingManagerType.ROLE_A, + ): + return "active controller event did not match its custom role" + var inactive_button := active_button.duplicate() as InputEventJoypadButton + inactive_button.device = manager.get_active_device_id() + 1 + if manager.event_matches_role( + inactive_button, + ControllerMappingManagerType.ROLE_A, + ): + return "inactive controller event matched the active profile" + var inactive_release := InputEventJoypadButton.new() + inactive_release.device = manager.get_active_device_id() + 2 + inactive_release.button_index = JOY_BUTTON_A + inactive_release.pressed = false + var active_device_before_release: int = manager.get_active_device_id() + manager._input(inactive_release) + if manager.get_active_device_id() != active_device_before_release: + return "inactive controller button release stole ownership" + for event: InputEvent in InputMap.action_get_events(&"jump"): + if ( + event is InputEventJoypadButton + and event.device != manager.get_active_device_id() + ): + return "custom InputMap event was not scoped to the active device" + manager._axis_rest_by_device["4"] = {str(JOY_AXIS_LEFT_X): 0.0} + var drift := InputEventJoypadMotion.new() + drift.device = 4 + drift.axis = JOY_AXIS_LEFT_X + drift.axis_value = 0.12 + if manager._axis_motion_claims_device(drift): + return "minor inactive-stick drift claimed controller ownership" + manager._input(drift) + if manager.get_active_device_id() != active_device_before_release: + return "minor inactive-stick drift changed the active controller" + drift.axis_value = 0.72 + if not manager._axis_motion_claims_device(drift): + return "intentional inactive-stick motion did not claim ownership" + manager._input(drift) + if manager.get_active_device_id() != drift.device: + return "intentional stick motion did not change controller ownership" + for event: InputEvent in InputMap.action_get_events(&"jump"): + if event is InputEventJoypadButton and event.device != drift.device: + return "active-device change did not rescope InputMap events" + manager._set_active_controller(active_device_before_release) + manager._axis_rest_by_device["5"] = {str(JOY_AXIS_LEFT_X): 0.0} + manager._on_joy_connection_changed(5, false) + if manager._axis_rest_by_device.has("5"): + return "disconnected controller retained stale axis calibration" + if not FileAccess.file_exists( + ControllerMappingManagerType.PROFILE_PATH + ): + return "controller mapping was not stored device-locally" + return "" + + +func _validate_portmaster_launcher() -> String: + const launcher_path: String = "res://scripts/portmaster/NETfishing.sh" + if not FileAccess.file_exists(launcher_path): + return "PortMaster launcher template is missing" + var launcher: String = FileAccess.get_file_as_string(launcher_path) + for expected_fragment: String in [ + "19004ca6010000000100000000010000", + "19000000010000000100000000010000", + "a:b0,b:b1,x:b3,y:b2", + "leftshoulder:b4,rightshoulder:b5", + "lefttrigger:b10,righttrigger:b11", + "guide:b8,start:b7,back:b6", + "leftstick:b9", + "rightstick:b12", + "netfishing_controllerconfig=\"$GODOT_MUOS_MAPPING\"", + "SDL_GAMECONTROLLERCONFIG=\"$netfishing_controllerconfig\" \\", + ]: + if expected_fragment not in launcher: + return "PortMaster launcher omitted " + expected_fragment + if "$'\\n'" in launcher: + return "PortMaster launcher appends mappings across a Weston-unsafe newline" + return "" + + +func _validate_auto_map(manager: ControllerMappingManagerType) -> String: + var panel := ControllerMappingPanelType.new() + root.add_child(panel) + panel.setup(manager) + panel.open_panel() + panel._begin_auto_map() + for role: StringName in ControllerMappingManagerType.ROLE_ORDER: + panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS) + var binding := ( + ControllerMappingManagerType.default_bindings()[str(role)] + as Dictionary + ) + if role == ControllerMappingManagerType.ROLE_B: + binding = ( + ControllerMappingManagerType.default_bindings()[ + str(ControllerMappingManagerType.ROLE_A) + ] as Dictionary + ) + if str(binding.get("kind", "")) == "button": + var button := InputEventJoypadButton.new() + button.device = manager.get_active_device_id() + button.button_index = int(binding.get("button", -1)) + button.pressed = true + manager.controller_input_observed.emit(button) + else: + var motion := InputEventJoypadMotion.new() + motion.device = manager.get_active_device_id() + motion.axis = int(binding.get("axis", -1)) + motion.axis_value = float(binding.get("direction", -1.0)) + manager.controller_input_observed.emit(motion) + if panel._auto_map_active: + return "auto-map did not complete after every requested input" + if not manager.has_custom_mapping(): + return "completed auto-map did not activate its profile" + if panel._binding_buttons.size() != ( + ControllerMappingManagerType.ROLE_ORDER.size() + ): + return "manual override list does not expose every mapped role" + var conflict_button := panel._binding_buttons.get( + ControllerMappingManagerType.ROLE_A + ) as Button + var conflict_style := conflict_button.get_theme_stylebox( + &"normal" + ) as StyleBoxFlat + if ( + conflict_style == null + or conflict_style.bg_color + != UtilityPageStyle.OCEAN_DANGER + ): + return "duplicate controller bindings were not marked in red" + manager.set_binding( + ControllerMappingManagerType.ROLE_B, + ControllerMappingManagerType.default_bindings()[ + str(ControllerMappingManagerType.ROLE_B) + ] as Dictionary, + ) + panel._begin_manual_capture( + ControllerMappingManagerType.ROLE_POINTER_MODIFIER + ) + panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS) + if "left trigger" in panel._progress_label.text.to_lower(): + return "manual remapping still dictates a specific physical input" + if "any button" not in panel._progress_label.text.to_lower(): + return "manual remapping does not request a generic controller input" + panel._cancel_capture() + panel._begin_auto_map() + panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS) + var lone_button := InputEventJoypadButton.new() + lone_button.device = manager.get_active_device_id() + lone_button.button_index = JOY_BUTTON_B + lone_button.pressed = true + manager.controller_input_observed.emit(lone_button) + if not panel._auto_map_active or panel._auto_map_index != 1: + return "a single controller button cancelled auto-map" + panel._cancel_capture() + panel._begin_auto_map() + panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS) + var left_bumper := InputEventJoypadButton.new() + left_bumper.device = manager.get_active_device_id() + left_bumper.button_index = JOY_BUTTON_LEFT_SHOULDER + left_bumper.pressed = true + manager.controller_input_observed.emit(left_bumper) + var right_bumper := InputEventJoypadButton.new() + right_bumper.device = manager.get_active_device_id() + right_bumper.button_index = JOY_BUTTON_RIGHT_SHOULDER + right_bumper.pressed = true + manager.controller_input_observed.emit(right_bumper) + panel._process(ControllerMappingPanelType.CANCEL_COMBO_HOLD_SECONDS - 0.01) + if not panel._auto_map_active: + return "bumper combo cancelled auto-map before the hold threshold" + panel._process(0.02) + if panel._auto_map_active or panel.is_capturing(): + return "sustained bumper combo did not cancel auto-map" + panel._begin_auto_map() + panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS) + var held_left := InputEventJoypadMotion.new() + held_left.device = manager.get_active_device_id() + held_left.axis = JOY_AXIS_LEFT_X + held_left.axis_value = -1.0 + for _index: int in 4: + manager.controller_input_observed.emit(held_left) + if panel._auto_map_index != 0: + return "auto-map accepted repeated analog motion without a neutral gate" + var first_button := InputEventJoypadButton.new() + first_button.device = manager.get_active_device_id() + first_button.button_index = JOY_BUTTON_A + first_button.pressed = true + manager.controller_input_observed.emit(first_button) + if panel._auto_map_index != 1 or not panel._waiting_for_neutral: + return "auto-map did not gate the next step after a captured input" + manager.controller_input_observed.emit(first_button) + if panel._auto_map_index != 1: + return "held input advanced more than one auto-map step" + panel._cancel_capture() + return "" + + +func _validate_virtual_mouse_bounds() -> String: + var window_size := Vector2(3840.0, 2160.0) + var bounds: Rect2 = GameUIType.get_virtual_mouse_window_bounds(window_size) + var expected_margin := Vector2(42.0, 42.0) + if not bounds.position.is_equal_approx(expected_margin): + return "4K virtual cursor minimum bounds are incorrect: %s" % bounds + if not bounds.end.is_equal_approx(window_size - expected_margin): + return "4K virtual cursor maximum bounds are incorrect: %s" % bounds + return "" + + +func _validate_controller_scroll_target() -> String: + var game_ui := GameUIType.new() + var page := VBoxContainer.new() + var scroll := ScrollContainer.new() + var content := VBoxContainer.new() + var button := Button.new() + var footer_button := Button.new() + root.add_child(page) + page.add_child(scroll) + scroll.add_child(content) + content.add_child(button) + page.add_child(footer_button) + var target: ScrollContainer = game_ui._focused_scroll_container(button) + if target != scroll: + game_ui.free() + page.free() + return "focused menu content did not resolve its scroll container" + target = game_ui._focused_scroll_container(footer_button) + if target != scroll: + game_ui.free() + page.free() + return "menu footer focus did not resolve the page's only scroll container" + scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + if game_ui._focused_scroll_container(button) != null: + game_ui.free() + page.free() + return "controller scrolling ignored a disabled scroll container" + game_ui.free() + page.free() + return "" + + +func _keyboard_event_count(action: StringName) -> int: + var count: int = 0 + for event: InputEvent in InputMap.action_get_events(action): + if event is InputEventKey or event is InputEventMouseButton: + count += 1 + return count diff --git a/tests/controller_mapping_validation.gd.uid b/tests/controller_mapping_validation.gd.uid new file mode 100644 index 0000000..7840fe3 --- /dev/null +++ b/tests/controller_mapping_validation.gd.uid @@ -0,0 +1 @@ +uid://dhwkjojmpjc13 diff --git a/tests/controller_ui_navigation_validation.gd b/tests/controller_ui_navigation_validation.gd new file mode 100644 index 0000000..7cecbab --- /dev/null +++ b/tests/controller_ui_navigation_validation.gd @@ -0,0 +1,82 @@ +extends SceneTree + +const ControllerFocusNavigationType = preload( + "res://ui/controller_focus_navigation.gd" +) +const UIReferencePresentationType = preload( + "res://ui/ui_reference_presentation.gd" +) + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + _validate_spatial_navigation() + _validate_controller_hierarchy_contract() + _validate_four_by_three_centering() + _validate_low_end_profile_contract() + print("Controller UI navigation validation: PASS") + quit() + + +func _validate_spatial_navigation() -> void: + var host := Control.new() + root.add_child(host) + var center := _make_button(host, "center", Vector2(200.0, 200.0)) + var left := _make_button(host, "left", Vector2(50.0, 200.0)) + var right := _make_button(host, "right", Vector2(350.0, 200.0)) + var top := _make_button(host, "top", Vector2(200.0, 50.0)) + var bottom := _make_button(host, "bottom", Vector2(200.0, 350.0)) + var controls: Array[Control] = [center, left, right, top, bottom] + ControllerFocusNavigationType.configure_spatial_neighbors(controls) + assert(center.get_node(center.focus_neighbor_left) == left) + assert(center.get_node(center.focus_neighbor_right) == right) + assert(center.get_node(center.focus_neighbor_top) == top) + assert(center.get_node(center.focus_neighbor_bottom) == bottom) + host.queue_free() + + +func _validate_controller_hierarchy_contract() -> void: + var source: String = FileAccess.get_file_as_string( + "res://ui/player_menu.gd" + ) + assert(source.contains("ROLE_POINTER_MODIFIER")) + assert(source.contains("ROLE_CAMERA_ZOOM")) + assert(source.contains("_handle_controller_secondary_switch")) + assert(source.contains("ROLE_LB")) + assert(source.contains("ROLE_RB")) + assert(source.contains("_reserve_main_navigation_for_page_switching")) + + +func _validate_four_by_three_centering() -> void: + var stage_position: Vector2 = ( + UIReferencePresentationType.get_stage_position(Vector2(640.0, 480.0)) + ) + assert(stage_position.is_equal_approx(Vector2(0.0, 120.0))) + + +func _validate_low_end_profile_contract() -> void: + var launcher: String = FileAccess.get_file_as_string( + "res://scripts/portmaster/NETfishing.sh" + ) + assert(launcher.contains("allwinner,h616")) + assert(launcher.contains("sun50iw9p1")) + assert(launcher.contains("NETFISHING_LOW_END=1")) + assert(launcher.contains("--max-fps 30")) + assert(launcher.contains("--audio-output-latency 40")) + + +func _make_button( + host: Control, + button_name: String, + button_position: Vector2, +) -> Button: + var button := Button.new() + button.name = button_name + button.position = button_position + button.size = Vector2(80.0, 80.0) + button.focus_mode = Control.FOCUS_ALL + host.add_child(button) + return button diff --git a/tests/controller_ui_navigation_validation.gd.uid b/tests/controller_ui_navigation_validation.gd.uid new file mode 100644 index 0000000..73ef752 --- /dev/null +++ b/tests/controller_ui_navigation_validation.gd.uid @@ -0,0 +1 @@ +uid://cbv0ia42rqu41 diff --git a/tests/dedicated_host_session_validation.gd b/tests/dedicated_host_session_validation.gd new file mode 100644 index 0000000..2d098bd --- /dev/null +++ b/tests/dedicated_host_session_validation.gd @@ -0,0 +1,60 @@ +extends SceneTree + +const TEST_PORT: int = 35777 +const OPERATOR_FINGERPRINT: String = ( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +) + + +func _init() -> void: + call_deferred("_run") + + +func _run() -> void: + var harness := Node.new() + root.add_child(harness) + var players := Node3D.new() + harness.add_child(players) + var spawn_service := PlayerSpawnService.new() + harness.add_child(spawn_service) + spawn_service.setup(players, null, Transform3D.IDENTITY) + var host_identity := HostIdentityStore.new() + harness.add_child(host_identity) + var known_players := KnownPlayerStore.new() + harness.add_child(known_players) + var host_bans := HostBanStore.new() + harness.add_child(host_bans) + var session := NetworkSession.new() + harness.add_child(session) + await process_frame + session.setup( + null, + null, + spawn_service, + null, + host_identity, + known_players, + null, + host_bans, + true, + ) + assert(session.configure_dedicated_operators( + PackedStringArray([OPERATOR_FINGERPRINT]) + )) + assert(session.start_dedicated_host(TEST_PORT, 5, "127.0.0.1")) + assert(session.is_dedicated_host()) + assert(session.get_local_peer_id() == 0) + assert(session.get_player_count() == 0) + assert(session.get_session_max_players() == 5) + assert(session.get_host_port() == TEST_PORT) + assert(bool(session.call( + "_operator_for_fingerprint", OPERATOR_FINGERPRINT + ))) + assert(session.set_host_open(true)) + assert(session.is_open_host()) + session.disconnect_session("Dedicated host validation complete.") + assert(not session.is_session_active()) + assert(not session.is_dedicated_host()) + harness.queue_free() + print("DEDICATED_HOST_SESSION_VALIDATION_OK") + quit(0) diff --git a/tests/dedicated_host_session_validation.gd.uid b/tests/dedicated_host_session_validation.gd.uid new file mode 100644 index 0000000..e50ebb3 --- /dev/null +++ b/tests/dedicated_host_session_validation.gd.uid @@ -0,0 +1 @@ +uid://ctky0gkb63nqa diff --git a/tests/dedicated_server_client_validation.gd b/tests/dedicated_server_client_validation.gd new file mode 100644 index 0000000..9688af2 --- /dev/null +++ b/tests/dedicated_server_client_validation.gd @@ -0,0 +1,49 @@ +extends SceneTree + +const MainScene = preload("res://main/main.tscn") +const TEST_ENDPOINT: String = "127.0.0.1:7777" +const EXPECTED_SERVER_NAME: String = "Dedicated-Client-Test" + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + root.size = Vector2i(1280, 720) + var main: Node = MainScene.instantiate() + root.add_child(main) + for _frame: int in 4: + await process_frame + if not bool(main.get("_application_initialized")): + main.call("_activate_selected_data_path", "", true) + for _frame: int in 8: + await process_frame + assert(bool(main.get("_application_initialized"))) + main.call("_on_title_join_game_requested", TEST_ENDPOINT) + var session := main.get_node("%NetworkSession") as NetworkSession + var joined: bool = false + var deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < deadline: + await process_frame + if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY: + main.call("_confirm_server_trust") + if session.is_joined_client() and bool(main.get("_gameplay_started")): + joined = true + break + assert(joined) + assert(session.get_local_peer_id() > 1) + assert(session.get_player_count() == 1) + var metadata: Dictionary = session.get_last_server_metadata() + assert(str(metadata.get("server_display_name", "")) == EXPECTED_SERVER_NAME) + var spawn_service := main.get_node( + "%PlayerSpawnService" + ) as PlayerSpawnService + assert(spawn_service.get_avatar(1) == null) + assert(spawn_service.get_avatar(session.get_local_peer_id()) != null) + print("DEDICATED_SERVER_CLIENT_VALIDATION_OK") + session.disconnect_session("Dedicated client validation complete.") + main.queue_free() + for _frame: int in 4: + await process_frame + quit() diff --git a/tests/dedicated_server_client_validation.gd.uid b/tests/dedicated_server_client_validation.gd.uid new file mode 100644 index 0000000..ed7a3b7 --- /dev/null +++ b/tests/dedicated_server_client_validation.gd.uid @@ -0,0 +1 @@ +uid://cwum3r4puerll diff --git a/tests/dedicated_server_config_validation.gd b/tests/dedicated_server_config_validation.gd new file mode 100644 index 0000000..6fef7c6 --- /dev/null +++ b/tests/dedicated_server_config_validation.gd @@ -0,0 +1,209 @@ +extends SceneTree + +const ConfigType = preload("res://server/dedicated_server_config.gd") +const OPERATOR_A: String = ( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +) +const OPERATOR_B: String = ( + "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210" +) + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var defaults := ConfigType.new() + defaults.set("data_directory", "/tmp/netfishing-server") + defaults.call("_validate") + assert(defaults.is_valid()) + assert(not bool(defaults.get("public_listing"))) + + var path: String = ProjectSettings.globalize_path( + "user://dedicated-server-validation.cfg" + ) + var file := ConfigFile.new() + file.set_value("server", "name", "Configured Room") + file.set_value("server", "bind_address", "127.0.0.1") + file.set_value("server", "port", 17777) + file.set_value("server", "max_players", 12) + file.set_value("server", "public", true) + file.set_value("server", "data_directory", "/tmp/configured-server") + file.set_value("discovery", "url", "https://discovery.netfishing.org/") + file.set_value( + "moderation", "operators", PackedStringArray([OPERATOR_B, OPERATOR_A]) + ) + assert(file.save(path) == OK) + + var configured := ConfigType.new() + assert(bool(configured.call("_load_file", path))) + configured.call("_validate") + assert(configured.is_valid()) + assert(str(configured.get("server_name")) == "Configured Room") + assert(str(configured.get("bind_address")) == "127.0.0.1") + assert(int(configured.get("port")) == 17777) + assert(int(configured.get("max_players")) == 12) + assert(bool(configured.get("public_listing"))) + assert(str(configured.get("discovery_url")) == "https://discovery.netfishing.org") + assert( + configured.get("operator_fingerprints") + == PackedStringArray([OPERATOR_A, OPERATOR_B]) + ) + assert(DirAccess.remove_absolute(path) == OK) + + var parsed := ConfigType.new() + parsed.set( + "operator_fingerprints", + ConfigType._parse_fingerprint_list( + "%s, %s;%s" % [OPERATOR_B.to_upper(), OPERATOR_A, OPERATOR_A] + ), + ) + parsed.set("data_directory", "/tmp/parsed-server") + parsed.call("_validate") + assert(parsed.is_valid()) + assert( + parsed.get("operator_fingerprints") + == PackedStringArray([OPERATOR_A, OPERATOR_B]) + ) + + var invalid_operator := ConfigType.new() + invalid_operator.set( + "operator_fingerprints", PackedStringArray(["not-a-fingerprint"]) + ) + invalid_operator.set("data_directory", "/tmp/invalid-operator-server") + invalid_operator.call("_validate") + assert(not invalid_operator.is_valid()) + + var invalid := ConfigType.new() + invalid.set("public_listing", true) + invalid.set("data_directory", "/tmp/invalid-server") + invalid.call("_validate") + assert(not invalid.is_valid()) + + var discovery := DiscoveryClient.new() + assert( + str(discovery.call("_default_room_name", "River")) + == "River's Server" + ) + assert( + DiscoveryClient.UPNP_RENEW_INTERVAL_SECONDS + < float(DiscoveryClient.UPNP_MAPPING_DURATION_SECONDS) + ) + assert( + DiscoveryClient.UPNP_RETRY_INTERVAL_SECONDS + < DiscoveryClient.UPNP_RENEW_INTERVAL_SECONDS + ) + var discovery_settings_path: String = ProjectSettings.globalize_path( + DiscoveryClient.SETTINGS_PATH + ) + assert(not FileAccess.file_exists(discovery_settings_path)) + assert(discovery.set_room_name("Client Room")) + var client_settings: PackedByteArray = FileAccess.get_file_as_bytes( + discovery_settings_path + ) + assert(not client_settings.is_empty()) + var dedicated_discovery := DiscoveryClient.new() + dedicated_discovery.call("_load_settings") + assert( + dedicated_discovery.configure_dedicated_runtime("Headless Room") + ) + assert(dedicated_discovery.get_room_name() == "Headless Room") + assert( + FileAccess.get_file_as_bytes(discovery_settings_path) + == client_settings + ) + dedicated_discovery.free() + assert(DirAccess.remove_absolute(discovery_settings_path) == OK) + assert( + discovery.get_host_state() + == DiscoveryClient.HostState.CLOSED + ) + assert( + discovery.get_public_join_state() + == DiscoveryClient.PublicJoinState.IDLE + ) + discovery.set("_lease_room_id", "local-room") + assert(discovery.is_own_room({"room_id": "local-room"})) + assert(not discovery.is_own_room({"room_id": "another-room"})) + discovery.set("_lease_room_id", "") + assert(not discovery.is_own_room({"room_id": "local-room"})) + var listed_room := { + "room_id": "empty-dedicated-room", + "room_name": "Empty Dedicated Room", + "address": "203.0.113.10", + "port": 7777, + "current_players": 0, + "max_players": 8, + "game_version": str(ProjectSettings.get_setting( + "application/config/version", "unknown" + )), + "protocol_version": NetworkProtocol.PROTOCOL_VERSION, + } + assert(bool(discovery.call("_valid_public_room", listed_room))) + var incompatible_room: Dictionary = listed_room.duplicate(true) + incompatible_room["game_version"] = "0.0.0-alpha" + assert(not bool(discovery.call("_valid_public_room", incompatible_room))) + assert( + NetworkProtocol.game_version_rejection( + "0.6.4-alpha", "0.6.5-alpha" + ) == NetworkProtocol.RejectionCode.CLIENT_OUTDATED + ) + assert( + NetworkProtocol.game_version_rejection( + "0.6.6-alpha", "0.6.5-alpha" + ) == NetworkProtocol.RejectionCode.SERVER_OUTDATED + ) + assert( + NetworkProtocol.game_version_rejection( + "0.6.5-alpha", "0.6.5-alpha" + ) == NetworkProtocol.RejectionCode.NONE + ) + assert( + NetworkProtocol.game_version_rejection( + "0.6.5-beta", "0.6.5-alpha" + ) == NetworkProtocol.RejectionCode.SERVER_OUTDATED + ) + var outdated_discovery_error := { + "error": { + "code": "game_version_mismatch", + "message": "generic mismatch", + "required_game_version": "99.0.0-alpha", + }, + } + var outdated_message: String = str( + discovery.call("_request_failure", outdated_discovery_error) + ) + assert("out of date" in outdated_message) + assert("99.0.0-alpha" in outdated_message) + assert("will not be listed" in outdated_message) + discovery.set( + "_host_request_kind", + DiscoveryClient.HostRequestKind.CREATE, + ) + discovery.set("_host_request_in_flight", true) + discovery.call( + "_on_host_request_completed", + HTTPRequest.RESULT_SUCCESS, + HTTPClient.RESPONSE_CONFLICT, + PackedStringArray(), + JSON.stringify(outdated_discovery_error).to_utf8_buffer(), + ) + assert(discovery.get_host_state() == DiscoveryClient.HostState.ERROR) + assert(discovery.host_status_is_error()) + assert(discovery.get_host_status_message() == outdated_message) + var newer_discovery_error := { + "error": { + "code": "game_version_mismatch", + "message": "generic mismatch", + "required_game_version": "0.0.0-alpha", + }, + } + var newer_message: String = str( + discovery.call("_request_failure", newer_discovery_error) + ) + assert("discovery is updated" in newer_message) + assert("will not be listed" in newer_message) + discovery.free() + print("DEDICATED_SERVER_CONFIG_VALIDATION_OK") + quit() diff --git a/tests/dedicated_server_config_validation.gd.uid b/tests/dedicated_server_config_validation.gd.uid new file mode 100644 index 0000000..4bc60d1 --- /dev/null +++ b/tests/dedicated_server_config_validation.gd.uid @@ -0,0 +1 @@ +uid://vm5lql2w6h3e diff --git a/tests/economy_regression_validation.gd b/tests/economy_regression_validation.gd index 0f54ca7..51ba48d 100644 --- a/tests/economy_regression_validation.gd +++ b/tests/economy_regression_validation.gd @@ -48,7 +48,7 @@ func _run() -> void: as PlayerAssetReservationService ) assert(player != null) - assert(catalog != null and catalog.candidates.size() == 8) + assert(catalog != null and catalog.candidates.size() == 53) assert(sale_service != null) assert(shop_service != null) assert(session != null and session.is_host()) @@ -82,7 +82,9 @@ func _run() -> void: print("Economy regression validation: PASS") session.disconnect_session("Economy validation complete.") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -96,7 +98,10 @@ func _run_multiplayer_host() -> void: var session := main.get_node("%NetworkSession") as NetworkSession assert(session.set_host_open(true)) var client_connected := false - var connection_deadline: int = Time.get_ticks_msec() + 30000 + # Loading the full client world can take longer on a cold import. Keep the + # host available until the client has completed authentication rather than + # timing out while the client is still starting. + var connection_deadline: int = Time.get_ticks_msec() + 60000 while Time.get_ticks_msec() < connection_deadline: await process_frame if session.get_authenticated_peer_ids().size() >= 2: @@ -126,7 +131,9 @@ func _run_multiplayer_host() -> void: print("Economy multiplayer host validation: PASS") session.disconnect_session("Economy host validation complete.") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -228,7 +235,9 @@ func _run_multiplayer_client() -> void: print("Economy multiplayer client validation: PASS") session.disconnect_session("Economy client validation complete.") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -407,7 +416,7 @@ func _test_player_menu_sale( assert(sell_action.mouse_filter == Control.MOUSE_FILTER_STOP) assert(sell_action.focus_mode == Control.FOCUS_ALL) assert(ui_viewport != null) - await _activate_focused_button(sell_action, ui_viewport) + await _activate_pointer_control(sell_action, ui_viewport) await process_frame assert(confirmation.visible) assert(confirm_button.visible and not confirm_button.disabled) @@ -416,7 +425,7 @@ func _test_player_menu_sale( > (player_menu.get_node("%CoolerOuterWall") as Control).z_index ) _sale_result.clear() - await _activate_focused_button(confirm_button, ui_viewport) + await _activate_pointer_control(confirm_button, ui_viewport) await process_frame assert(not _sale_result.is_empty() and bool(_sale_result[1])) assert(not player.inventory.contains_catch_id(fish_catch.catch_id)) @@ -435,18 +444,29 @@ func _test_player_menu_sale( await create_timer(2.2).timeout -func _activate_focused_button( - button: Button, +func _activate_pointer_control( + control: Control, ui_viewport: SubViewport, ) -> void: - button.grab_focus() + var presenter := ui_viewport.get_parent() as SubViewportContainer + assert(presenter != null) + var local_center: Vector2 = control.get_global_transform_with_canvas() * ( + control.size * 0.5 + ) + var center: Vector2 = presenter.position + local_center * presenter.scale + var motion := InputEventMouseMotion.new() + motion.position = center + motion.global_position = center + root.push_input(motion, true) await process_frame - assert(button.has_focus()) + assert(ui_viewport.gui_get_hovered_control() == control) for is_pressed: bool in [true, false]: - var accept := InputEventAction.new() - accept.action = &"ui_accept" - accept.pressed = is_pressed - ui_viewport.push_input(accept, false) + var click := InputEventMouseButton.new() + click.button_index = MOUSE_BUTTON_LEFT + click.position = center + click.global_position = center + click.pressed = is_pressed + root.push_input(click, true) await process_frame @@ -495,6 +515,8 @@ func _test_host_art_shop_purchase( ArtShopStock.ART_KIT_ITEM_ID ) assert(art_item != null and art_item.hotbar_allowed and art_item.equippable) + assert(art_item.icon != null) + assert(art_item.icon.resource_path.ends_with("/art/art_kit.png")) assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID)) for product_id: StringName in [ &"marker_ocean_teal", &"brush_2x", &"grid_32x", @@ -552,20 +574,94 @@ func _test_fishing_shop_sale_ui( var shop_panel_style := shop_panel.get_theme_stylebox("panel") as StyleBoxFlat assert(shop_panel_style != null) assert(shop_panel_style.corner_radius_top_left == 24) - var buy_mode := shop.get_node("%BuyModeButton") as Button - var sell_mode := shop.get_node("%SellModeButton") as Button - assert(buy_mode.visible and sell_mode.visible) + var upgrade_grid := shop.get_node("%UpgradeGrid") as GridContainer + assert(upgrade_grid != null and upgrade_grid.columns == 3) + assert((shop.get_node("%Upgrades") as Control).visible) + assert(not (shop.get_node("%Supplies") as Control).visible) + for upgrade_name: String in [ + "ReelPurchase", "BarrierPurchase", "CoolerPurchase" + ]: + var upgrade_button := shop.get_node("%%%s" % upgrade_name) as Button + assert(upgrade_button != null) + assert(upgrade_button.size == Vector2(144.0, 144.0)) + assert(upgrade_button.text.is_empty()) + assert(upgrade_button.icon != null) + assert(upgrade_button.tooltip_text.contains("level")) + assert(not shop.has_node("%ReelLevel")) + assert(not shop.has_node("%BarrierEffect")) + assert(not shop.has_node("%CoolerLevel")) + var balance_coin := shop.get_node( + "ShopPanel/Margin/Layout/Header/BalanceDisplay/CoinIcon" + ) as TextureRect + assert(balance_coin != null and balance_coin.texture != null) + assert( + balance_coin.texture.resource_path.ends_with( + "/shop/32_currency.png" + ) + ) + for cost_name: String in ["ReelCost", "BarrierCost", "CoolerCost"]: + var cost_display := shop.get_node("%%%s" % cost_name) as CurrencyAmount + assert(cost_display != null) + var cost_icon := cost_display.get_node("Icon") as TextureRect + assert(cost_icon != null and cost_icon.texture != null) + assert( + cost_icon.texture.resource_path.ends_with( + "/shop/32_currency.png" + ) + ) + assert(not shop.has_node("ShopPanel/Margin/Layout/ModeTabs")) + var shop_tabs: Array = shop.get("_shop_tabs") as Array + assert(shop_tabs.size() == 6) + var art_supplies_tab := shop_tabs[4] as Button + assert(art_supplies_tab != null and art_supplies_tab.text == "Art Supplies") + var sell_mode := shop_tabs[5] as Button + assert(sell_mode != null and sell_mode.text == "Sell Fish") + await _activate_pointer_control(art_supplies_tab, ui_viewport) + await process_frame var stock_sections: Array[String] = [] for child: Node in shop.get_node("%SuppliesList").get_children(): if child is Label: stock_sections.append((child as Label).text) - assert(stock_sections == ["supplies", "art kit", "markers", "brushes", "grids"]) - await _activate_focused_button(sell_mode, ui_viewport) + assert(stock_sections == ["art kit", "markers", "brushes", "grids"]) + var marker_icons := shop.find_children( + "MarkerIcon", "TextureRect", true, false + ) + assert(marker_icons.size() == ArtShopStock.MARKER_PRODUCTS.size()) + for index: int in marker_icons.size(): + var marker_icon := marker_icons[index] as TextureRect + var marker_material := marker_icon.material as ShaderMaterial + var product_id: StringName = ArtShopStock.MARKER_PRODUCTS[index] + var color_id: StringName = PlayerArtUnlocks.color_id_for_product( + product_id + ) + assert(marker_icon.texture.resource_path.ends_with("art_kit_marker.png")) + assert(marker_material != null) + assert( + marker_material.get_shader_parameter("marker_color") + == SurfaceDrawingPalette.get_color(color_id) + ) + var price_bubbles := shop.find_children( + "PriceBubble", "PanelContainer", true, false + ) + assert(not price_bubbles.is_empty()) + for price_bubble: Node in price_bubbles: + var currency_icon := price_bubble.find_child( + "CurrencyIcon", true, false + ) as TextureRect + assert(currency_icon != null and currency_icon.texture != null) + assert( + currency_icon.texture.resource_path.ends_with( + "/shop/32_currency.png" + ) + ) + await _activate_pointer_control(sell_mode, ui_viewport) await process_frame assert(shop.visible and not player_menu.visible) assert(not shop.has_node("ShopPanel/Margin/Layout/Body/FishSales")) assert((shop.get_node("%ShopCoolerPage") as Control).visible) - assert(not (shop.get_node("%ShopPanel") as Control).visible) + assert((shop.get_node("%ShopPanel") as Control).visible) + assert(not (shop.get_node("ShopPanel/Margin/Layout/Body") as Control).visible) + assert(not (shop.get_node("%Feedback") as Control).visible) var mounted_cooler := player_menu.get("_cooler_page") as Control assert(mounted_cooler != null and mounted_cooler.visible) assert( @@ -575,6 +671,11 @@ func _test_fishing_shop_sale_ui( var water_surface := player_menu.get("_cooler_water_surface") as ColorRect assert(cooler_outer_wall != null and cooler_outer_wall.visible) assert(water_surface.visible and water_surface.material is ShaderMaterial) + var cooler_sort_option := player_menu.get("_cooler_sort_option") as Control + await _activate_pointer_control(cooler_sort_option, ui_viewport) + var cooler_choice_panel := cooler_sort_option.get("_choice_panel") as Control + assert(cooler_choice_panel.visible) + cooler_sort_option.call("close_choices") assert( StringName( (player_menu.get("_sale_buyer_override") as FishBuyerProfile).id @@ -585,10 +686,10 @@ func _test_fishing_shop_sale_ui( var reserved_button := fish_nodes.get(reserved_catch.catch_id) as Button assert(fish_button != null and fish_button.visible) assert(reserved_button != null and reserved_button.visible) - await _activate_focused_button(fish_button, ui_viewport) + await _activate_pointer_control(fish_button, ui_viewport) var sell_button := player_menu.get("_sell_bubble") as Button assert(sell_button.visible and not sell_button.disabled) - await _activate_focused_button(sell_button, ui_viewport) + await _activate_pointer_control(sell_button, ui_viewport) await process_frame var confirmation := player_menu.get("_sale_confirmation") as Control var confirm_button := player_menu.get("_confirm_sale_button") as Button @@ -598,21 +699,21 @@ func _test_fishing_shop_sale_ui( > cooler_outer_wall.z_index ) _sale_result.clear() - await _activate_focused_button(confirm_button, ui_viewport) + await _activate_pointer_control(confirm_button, ui_viewport) await process_frame assert(not _sale_result.is_empty() and bool(_sale_result[1])) assert(not player.inventory.contains_catch_id(fish_catch.catch_id)) assert(player.wallet.get_balance() == balance_before + fish_catch.sale_value) assert(not sale_service.is_local_sale_pending()) assert(reservations.release(reservation_id)) - var back_button := shop.get_node("%BackToShopButton") as Button - await _activate_focused_button(back_button, ui_viewport) + await _activate_pointer_control(shop_tabs[0] as Button, ui_viewport) await process_frame assert(shop.visible and (shop.get_node("%ShopPanel") as Control).visible) assert(not (shop.get_node("%ShopCoolerPage") as Control).visible) assert(not player_menu.is_shop_cooler_mounted()) shop.close_shop() - await process_frame + await shop.menu_visibility_changed + assert(not shop.visible) func _assert_sale( @@ -633,7 +734,9 @@ func _assert_sale( assert(bool(_sale_result[1]) == expected_success) assert(not sale_service.is_local_sale_pending()) if expected_success: - assert(player.wallet.get_balance() > balance_before) + var payout: int = int(_sale_result[4]) + assert(payout >= 0) + assert(player.wallet.get_balance() == balance_before + payout) for catch_id: StringName in catch_ids: assert(not player.inventory.contains_catch_id(catch_id)) else: diff --git a/tests/exported_decal_hotfix_validation.gd b/tests/exported_decal_hotfix_validation.gd new file mode 100644 index 0000000..fe279b0 --- /dev/null +++ b/tests/exported_decal_hotfix_validation.gd @@ -0,0 +1,77 @@ +extends SceneTree + +const FEATURE_ASSET_ROOTS: Dictionary = { + "eyes": "res://art/exported/characters/faces/eyes", + "mouth": "res://art/exported/characters/faces/mouth", + "nose": "res://art/exported/characters/faces/noses", +} + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + assert( + CharacterCustomizationCatalog._canonical_feature_filename( + "eyes_alligator_eyes.png" + ) == "eyes_alligator_eyes.png" + ) + assert( + CharacterCustomizationCatalog._canonical_feature_filename( + "eyes_alligator_eyes.png.import" + ) == "eyes_alligator_eyes.png" + ) + assert( + CharacterCustomizationCatalog._canonical_feature_filename( + "eyes_alligator_eyes.png.remap" + ) == "eyes_alligator_eyes.png" + ) + assert( + CharacterCustomizationCatalog._canonical_feature_filename( + "eyes_alligator_eyes.jpg" + ).is_empty() + ) + + for category_id: String in FEATURE_ASSET_ROOTS: + var options := CharacterCustomizationCatalog.options_for(category_id) + var expected_ids: Dictionary = _feature_ids_on_disk(category_id) + assert(options.size() == expected_ids.size() + 1) + var seen_ids: Dictionary = {} + for option: Dictionary in options: + var option_id := str(option.get("id", "")) + assert(not seen_ids.has(option_id)) + seen_ids[option_id] = true + if option_id != "none": + assert( + CharacterCustomizationCatalog.texture_for( + category_id, option_id + ) != null + ) + for expected_id: String in expected_ids: + assert(seen_ids.has(expected_id)) + for mouth_id: String in ["open_ah", "open_oh", "open_smile"]: + assert(CharacterCustomizationCatalog.texture_for( + "mouth", mouth_id + ) != null) + + print("Exported decal hotfix validation: PASS") + quit() + + +func _feature_ids_on_disk(category_id: String) -> Dictionary: + var result: Dictionary = {} + var root_path: String = str(FEATURE_ASSET_ROOTS[category_id]) + var directory := DirAccess.open(root_path) + assert(directory != null) + for file_name: String in directory.get_files(): + if not file_name.to_lower().ends_with(".png"): + continue + var option_id: String = ( + CharacterCustomizationCatalog._feature_id_from_filename( + category_id, file_name + ) + ) + if not option_id.is_empty(): + result[option_id] = true + return result diff --git a/tests/exported_decal_hotfix_validation.gd.uid b/tests/exported_decal_hotfix_validation.gd.uid new file mode 100644 index 0000000..e75742a --- /dev/null +++ b/tests/exported_decal_hotfix_validation.gd.uid @@ -0,0 +1 @@ +uid://dolsbknm52hsy diff --git a/tests/fish_catalog_content_validation.gd b/tests/fish_catalog_content_validation.gd index 2a74663..6746d70 100644 --- a/tests/fish_catalog_content_validation.gd +++ b/tests/fish_catalog_content_validation.gd @@ -40,8 +40,36 @@ const CATFISH_IDS: Array[StringName] = [ const FRESH_WATER_IDS: Array[StringName] = [ &"bluegill", &"carp", &"catfish_blue", &"catfish_channel", &"catfish_flathead", &"catfish_white", + &"goby_round", +] +const SALT_WATER_IDS: Array[StringName] = [ + &"bass", + &"sunfish", + &"tuna_albacore", + &"tuna_bigeye", + &"tuna_bluefin", + &"tuna_skipjack", + &"tuna_yellowfin", + &"salmon_atlantic", + &"salmon_chum", + &"salmon_coho", + &"salmon_pink", + &"salmon_sockeye", + &"anchovy_european", &"anchovy_northern", + &"grouper_gulf", &"grouper_red", + &"mackerel_atlantic", &"mackerel_cero", &"mackerel_chub", + &"mackerel_king", &"mackerel_spanish", + &"marlin_black", &"marlin_blue", &"marlin_white", + &"pomfret_black", &"pomfret_chinese", &"pomfret_golden", + &"pomfret_white", &"sailfish", + &"snapper_lane", &"snapper_mangrove", &"snapper_mutton", + &"snapper_red", &"swordfish", +] +const NEW_FRESH_WATER_IDS: Array[StringName] = [ + &"chub_european", &"chub_flame", &"chub_lake", &"goldfish", + &"goldfish_bubbleeye", &"sauger", &"saugeye", &"trout_cutthroat", + &"trout_golden", &"trout_rainbow", &"trout_steelhead", &"walleye", ] -const SALT_WATER_IDS: Array[StringName] = [&"bass", &"sunfish"] func _initialize() -> void: @@ -50,6 +78,7 @@ func _initialize() -> void: func _run() -> void: _validate_catalog_and_pools() + _validate_weight_based_display_scale() _validate_starter_water_bodies() _validate_authoritative_water_filter() _validate_catches_and_authoritative_sale() @@ -57,10 +86,42 @@ func _run() -> void: quit() +func _validate_weight_based_display_scale() -> void: + var previous_scale: float = 0.0 + for fish: FishDataType in Catalog.candidates: + assert(fish.is_selectable()) + assert(fish.weight_min_lb > 0.0) + assert(fish.weight_max_lb <= 1000.0) + var minimum_scale: float = fish.get_display_scale_for_weight( + fish.weight_min_lb + ) + var maximum_scale: float = fish.get_display_scale_for_weight( + fish.weight_max_lb + ) + assert(minimum_scale >= FishDataType.DISPLAY_MIN_SCALE) + assert(maximum_scale <= FishDataType.DISPLAY_MAX_SCALE) + assert(maximum_scale >= minimum_scale) + var midpoint_weight: float = lerpf( + fish.weight_min_lb, + fish.weight_max_lb, + 0.5, + ) + assert( + fish.get_display_scale_for_weight(midpoint_weight) + >= minimum_scale + ) + # A one-pound catch is a shared visual reference, not a per-species + # texture-size decision. + var reference_scale: float = fish.get_display_scale_for_weight(1.0) + assert(is_equal_approx(reference_scale, FishDataType.DISPLAY_REFERENCE_SCALE)) + previous_scale = maxf(previous_scale, maximum_scale) + assert(previous_scale <= FishDataType.DISPLAY_MAX_SCALE) + + func _validate_catalog_and_pools() -> void: - assert(Catalog.candidates.size() == 8) - assert(PondPool.candidates.size() == 6) - assert(OceanPool.candidates.size() == 2) + assert(Catalog.candidates.size() == 53) + assert(PondPool.candidates.size() == 19) + assert(OceanPool.candidates.size() == 34) for fish_id: StringName in ORIGINAL_IDS: var original_fish: FishDataType = Catalog.get_fish_by_id(fish_id) assert(original_fish != null) @@ -87,6 +148,12 @@ func _validate_catalog_and_pools() -> void: assert(OceanPool.get_fish_by_id(fish_id) == null) assert(fish.availability.allowed_location_tags == [&"starter_pond"]) assert(LogbookCatalog.category_for(fish) == WaterType.Type.FRESH_WATER) + for fish_id: StringName in NEW_FRESH_WATER_IDS: + var fish: FishDataType = Catalog.get_fish_by_id(fish_id) + assert(fish != null and fish.is_selectable()) + assert(PondPool.get_fish_by_id(fish_id) == fish) + assert(OceanPool.get_fish_by_id(fish_id) == null) + assert(LogbookCatalog.category_for(fish) == WaterType.Type.FRESH_WATER) var expected_values: Dictionary[StringName, Array] = { &"catfish_blue": [1, 1.25, 3.0, 12.0, 6, 9], @@ -104,6 +171,43 @@ func _validate_catalog_and_pools() -> void: assert(fish.sell_value_min == int(values[4])) assert(fish.sell_value_max == int(values[5])) + var expected_tuna_values: Dictionary[StringName, Array] = { + &"tuna_albacore": [2, 1.5, 10.0, 45.0, 12, 24], + &"tuna_bigeye": [3, 0.55, 20.0, 120.0, 22, 48], + &"tuna_bluefin": [4, 0.18, 30.0, 250.0, 35, 90], + &"tuna_skipjack": [1, 3.0, 5.0, 25.0, 8, 16], + &"tuna_yellowfin": [2, 1.0, 15.0, 80.0, 16, 36], + } + for fish_id: StringName in expected_tuna_values: + var fish: FishDataType = Catalog.get_fish_by_id(fish_id) + var values: Array = expected_tuna_values[fish_id] + assert(fish != null and fish.is_selectable()) + assert(int(fish.rarity) == int(values[0])) + assert(is_equal_approx(fish.base_catch_weight, float(values[1]))) + assert(is_equal_approx(fish.weight_min_lb, float(values[2]))) + assert(is_equal_approx(fish.weight_max_lb, float(values[3]))) + assert(fish.sell_value_min == int(values[4])) + assert(fish.sell_value_max == int(values[5])) + + var expected_new_values: Dictionary[StringName, Array] = { + &"goby_round": [0, 8.0, 0.1, 0.6, 2, 3], + &"salmon_atlantic": [3, 0.65, 8.0, 40.0, 20, 42], + &"salmon_chum": [1, 2.5, 6.0, 25.0, 10, 22], + &"salmon_coho": [2, 1.4, 5.0, 30.0, 14, 28], + &"salmon_pink": [1, 3.0, 3.0, 12.0, 8, 15], + &"salmon_sockeye": [2, 1.6, 4.0, 15.0, 12, 24], + } + for fish_id: StringName in expected_new_values: + var fish: FishDataType = Catalog.get_fish_by_id(fish_id) + var values: Array = expected_new_values[fish_id] + assert(fish != null and fish.is_selectable()) + assert(int(fish.rarity) == int(values[0])) + assert(is_equal_approx(fish.base_catch_weight, float(values[1]))) + assert(is_equal_approx(fish.weight_min_lb, float(values[2]))) + assert(is_equal_approx(fish.weight_max_lb, float(values[3]))) + assert(fish.sell_value_min == int(values[4])) + assert(fish.sell_value_max == int(values[5])) + var pond_context := FishingContextType.new() pond_context.location_tags = [&"starter_pond"] pond_context.water_type = WaterType.Type.FRESH_WATER @@ -209,7 +313,12 @@ func _validate_starter_water_bodies() -> void: assert(pond.fish_pool == PondPool) assert(ocean.fish_pool == OceanPool) assert(region.get_node_or_null("ShorelineRibbons/Pond") == null) - assert(region.get_node_or_null("ShorelineRibbons/Ocean") != null) + var shoreline_geometry := ( + region.get_node_or_null("ShorelineRibbons/Ocean") as MeshInstance3D + ) + assert(shoreline_geometry != null) + assert(not shoreline_geometry.visible) + assert(shoreline_geometry.material_override == null) var pond_material := ( region.get_node("WaterBodies/Pond/VisualWater") as MeshInstance3D ).material_override as ShaderMaterial @@ -251,6 +360,12 @@ func _validate_catches_and_authoritative_sale() -> void: assert(loaded != null) assert(loaded.fish_id == fish.id) assert(loaded.fish == fish) + assert( + is_equal_approx( + loaded.display_scale, + fish.get_display_scale_for_weight(loaded.weight_lb), + ) + ) var replicated: FishCatch = FishCatchType.from_network_dict( fish_catch.to_network_dict(), Catalog.get_fish_by_id(fish.id) @@ -258,6 +373,12 @@ func _validate_catches_and_authoritative_sale() -> void: assert(replicated != null) assert(replicated.fish_id == fish.id) assert(replicated.fish.display_texture == fish.display_texture) + assert( + is_equal_approx( + replicated.display_scale, + fish.get_display_scale_for_weight(replicated.weight_lb), + ) + ) inventory.add_catch(loaded) assert(inventory.contains_catch_id(loaded.catch_id)) diff --git a/tests/fish_hotbar_showcase_validation.gd b/tests/fish_hotbar_showcase_validation.gd index bd1df0f..06b2f4d 100644 --- a/tests/fish_hotbar_showcase_validation.gd +++ b/tests/fish_hotbar_showcase_validation.gd @@ -3,7 +3,6 @@ extends SceneTree const MainScene = preload("res://main/main.tscn") const FishCatchType = preload("res://fish/fish_catch.gd") const FishDataType = preload("res://fish/fish_data.gd") -const FishingSpotType = preload("res://fishing/fishing_spot.gd") func _initialize() -> void: @@ -31,13 +30,15 @@ func _run() -> void: var player := main.get("_player") as Player var world_time := main.get_node("%WorldTimeService") as WorldTimeService + var world_weather := ( + main.get_node("%WorldWeatherService") as WorldWeatherService + ) world_time.synchronize_time(19.75) assert(player.experience.award_experience(125)) var fish_catalog := main.get("fish_catalog") as FishPool var service := main.get_node( "%NetworkFishShowcaseService" ) as NetworkFishShowcaseService - var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType assert(player != null and fish_catalog != null and service != null) var fish: FishDataType = fish_catalog.get_fish_by_id(&"bluegill") assert(fish != null) @@ -80,18 +81,18 @@ func _run() -> void: assert(player.hotbar.get_selected_fish_catch_id() == fish_catch.catch_id) assert(player.hotbar.get_selected_item_id().is_empty()) - var press := InputEventMouseButton.new() - press.button_index = MOUSE_BUTTON_LEFT - press.pressed = true - fishing_spot.call("_unhandled_input", press) - await process_frame assert(service.is_local_showcase_visible()) assert(service.get_local_showcase_catch_id() == fish_catch.catch_id) - assert((player.get_node("%HeldFishDisplay") as Node3D).visible) - fishing_spot.call("_unhandled_input", press) + await _wait_for_held_fish_visibility(player, true) + assert(service.toggle_selected_fish()) await process_frame assert(not service.is_local_showcase_visible()) - assert(not (player.get_node("%HeldFishDisplay") as Node3D).visible) + await _wait_for_held_fish_visibility(player, false) + var saved_weather: WorldWeatherService.Weather = ( + world_weather.get_weather() + ) + var saved_weather_seconds: float = world_weather.get_seconds_remaining() + var saved_time_hours: float = world_time.get_time_hours() assert(save_manager.save_now()) var save_path: String = str(save_manager.get("_save_path")) @@ -103,17 +104,25 @@ func _run() -> void: var hotbar_data: Dictionary = (parsed as Dictionary)["hotbar"] assert(typeof(hotbar_data.get("fish_slots")) == TYPE_ARRAY) assert(str((hotbar_data["fish_slots"] as Array)[1]) == fish_catch.catch_id) - assert(int((parsed as Dictionary)["save_version"]) == 6) + assert(int((parsed as Dictionary)["save_version"]) == 7) assert( int((parsed as Dictionary)["experience"]["total_experience"]) == 125 ) + assert(absf( + float((parsed as Dictionary)["world"]["time_hours"]) - saved_time_hours + ) < 0.01) assert( - is_equal_approx( - float((parsed as Dictionary)["world"]["time_hours"]), - 19.75, - ) + int((parsed as Dictionary)["world"]["weather"]) + == int(saved_weather) ) + assert(absf( + float( + (parsed as Dictionary)["world"][ + "weather_seconds_remaining" + ] + ) - saved_weather_seconds + ) < 0.1) var saved_catches: Array = (parsed as Dictionary)["inventory"]["catches"] assert(int((saved_catches[0] as Dictionary)["quality"]) == fish_catch.quality) var saved_masks: Dictionary = ( @@ -127,9 +136,18 @@ func _run() -> void: assert(player.hotbar.clear_slot(1)) assert(player.experience.restore_total_experience(0)) world_time.synchronize_time(8.0) + world_weather.clear_daily_plan() + world_weather.apply_authoritative_snapshot( + WorldWeatherService.Weather.FOGGY, + 10.0, + ) assert(save_manager.load_player_data()) assert(player.experience.get_total_experience() == 125) - assert(is_equal_approx(world_time.get_time_hours(), 19.75)) + assert(absf(world_time.get_time_hours() - saved_time_hours) < 0.01) + assert(world_weather.get_weather() == saved_weather) + assert(absf( + world_weather.get_seconds_remaining() - saved_weather_seconds + ) < 2.0) assert(player.experience.get_level() == 2) assert(player.hotbar.get_fish_catch_id(1) == fish_catch.catch_id) assert(player.hotbar.get_selected_slot() == 1) @@ -139,14 +157,13 @@ func _run() -> void: FishQuality.Tier.EXCEPTIONAL, ) ) - assert(not service.is_local_showcase_visible()) - assert(service.toggle_selected_fish()) assert(service.is_local_showcase_visible()) + await _wait_for_held_fish_visibility(player, true) assert(player.inventory.remove_catch_by_id(fish_catch.catch_id) != null) await process_frame assert(player.hotbar.get_fish_catch_id(1).is_empty()) assert(not service.is_local_showcase_visible()) - assert(not (player.get_node("%HeldFishDisplay") as Node3D).visible) + await _wait_for_held_fish_visibility(player, false) var valid_state: Dictionary = { "session_id": "session", @@ -164,7 +181,7 @@ func _run() -> void: var invalid_state: Dictionary = valid_state.duplicate(true) invalid_state["display_scale"] = 1000.0 assert(not NetworkFishShowcaseProtocol.validate_state(invalid_state)) - assert(NetworkProtocol.PROTOCOL_VERSION == 3) + assert(NetworkProtocol.PROTOCOL_VERSION == 4) assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10) assert( NetworkProtocol.FISH_QUALITY_CAPABILITY @@ -178,5 +195,26 @@ func _run() -> void: print("Fish hotbar showcase validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() + + +func _wait_for_held_fish_visibility( + player: Player, + expected_visible: bool, +) -> void: + var held_fish_display := player.get("_held_fish_display") as Node3D + assert(held_fish_display != null) + var deadline_msec: int = Time.get_ticks_msec() + 2000 + while ( + held_fish_display.visible != expected_visible + and Time.get_ticks_msec() < deadline_msec + ): + await process_frame + assert( + held_fish_display.visible == expected_visible, + "Held fish display did not become %s within the timeout." + % expected_visible, + ) diff --git a/tests/fish_quality_validation.gd b/tests/fish_quality_validation.gd index e4f3288..555fb3c 100644 --- a/tests/fish_quality_validation.gd +++ b/tests/fish_quality_validation.gd @@ -19,11 +19,13 @@ func _initialize() -> void: func _run() -> void: _validate_tiers_and_distribution() + _validate_barrier_challenge_curve() + _validate_fight_pacing_and_reel_upgrades() _validate_catch_round_trip_and_sale() _validate_mail_round_trip() _validate_collection_mastery() _validate_version_four_migration() - assert(NetworkProtocol.PROTOCOL_VERSION == 3) + assert(NetworkProtocol.PROTOCOL_VERSION == 4) assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10) assert(NetworkProtocol.FISH_QUALITY_CAPABILITY == "fish_quality_v1") print("Fish quality validation: PASS") @@ -72,6 +74,167 @@ func _validate_tiers_and_distribution() -> void: assert(absf(observed - expected) < 0.015) +func _validate_barrier_challenge_curve() -> void: + assert( + FishQualityType.BARRIER_HEALTH_MULTIPLIERS.size() + == FishQualityType.TIER_COUNT + ) + var expected_minimums: Array[int] = [1, 10, 50, 100, 200] + var expected_maximums: Array[int] = [9, 50, 100, 200, 400] + var previous_health: int = 0 + for quality: int in FishQualityType.TIER_COUNT: + var expected_range := Vector2i( + expected_minimums[quality], expected_maximums[quality] + ) + assert( + FishQualityType.barrier_health_range(quality) + == expected_range + ) + assert( + FishQualityType.roll_barrier_health(null, quality) + == expected_range.x + ) + var rng := RandomNumberGenerator.new() + rng.seed = 73013 + quality + var observed_counts: Dictionary[int, int] = {} + var observed_minimum: int = expected_range.y + var observed_maximum: int = expected_range.x + var observed_total: int = 0 + var band_size: int = expected_range.y - expected_range.x + 1 + var sample_count: int = maxi(10000, band_size * 250) + for _sample: int in sample_count: + var rolled_health: int = FishQualityType.roll_barrier_health( + rng, quality + ) + assert( + rolled_health >= expected_range.x + and rolled_health <= expected_range.y + ) + observed_counts[rolled_health] = ( + int(observed_counts.get(rolled_health, 0)) + 1 + ) + observed_minimum = mini(observed_minimum, rolled_health) + observed_maximum = maxi(observed_maximum, rolled_health) + observed_total += rolled_health + assert(observed_minimum == expected_range.x) + assert(observed_maximum == expected_range.y) + for authored_health: int in range( + expected_range.x, expected_range.y + 1 + ): + assert(observed_counts.has(authored_health)) + var observed_average: float = ( + float(observed_total) / float(sample_count) + ) + var authored_midpoint: float = ( + float(expected_range.x + expected_range.y) / 2.0 + ) + assert( + absf(observed_average - authored_midpoint) + < maxf(0.15, float(band_size) * 0.01) + ) + var health: int = FishQualityType.apply_barrier_health(8, quality) + assert(health > previous_health) + previous_health = health + assert(FishQualityType.apply_barrier_health(8, -1) == 8) + assert(FishQualityType.apply_barrier_health(0, FishQualityType.Tier.SHINY) == 4) + assert( + FishQualityType.barrier_health_range(-1) + == Vector2i(1, 9) + ) + + var profile := CatchDifficultyProfile.new() + profile.barrier_count_min = 1 + profile.barrier_count_max = 1 + profile.first_barrier_margin = 0.2 + profile.final_barrier_margin = 0.2 + profile.minimum_barrier_spacing = 0.1 + var controller := CatchController.new() + root.add_child(controller) + for quality: int in FishQualityType.TIER_COUNT: + controller.start_authoritative_encounter( + profile, + 0.1, + 1, + 818181, + quality, + ) + var barriers_value: Variant = controller.get("_barriers") + assert(barriers_value is Array) + var barriers: Array = barriers_value as Array + assert(barriers.size() == 1) + var barrier := barriers[0] as RefCounted + assert(barrier != null) + var health: int = int(barrier.get("maximum_health")) + assert(health >= expected_minimums[quality]) + assert(health <= expected_maximums[quality]) + controller.queue_free() + + var shiny_health: int = FishQualityType.barrier_health_range( + FishQualityType.Tier.SHINY + ).y + var base_power_clicks: int = ceili(float(shiny_health) / 1.0) + var max_power_clicks: int = ceili( + float(shiny_health) + / float(PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL + 1) + ) + assert(base_power_clicks == 400) + assert(max_power_clicks == 100) + assert(max_power_clicks < base_power_clicks) + + +func _validate_fight_pacing_and_reel_upgrades() -> void: + assert(is_equal_approx(CatchController.CHASE_SPEED, 0.07)) + assert(is_equal_approx(CatchController.CHASE_START_DELAY, 1.0)) + assert(is_equal_approx(CatchController.CHASE_START_OFFSET, 0.04)) + assert(is_equal_approx(Player.BASE_REEL_SPEED, 0.16)) + + var upgrades := PlayerFishingUpgrades.new() + assert(is_equal_approx(upgrades.get_reel_speed_multiplier(), 1.0)) + assert( + upgrades.restore_levels( + PlayerFishingUpgrades.MAX_REEL_SPEED_LEVEL, + 0, + ) + ) + var upgraded_multiplier: float = upgrades.get_reel_speed_multiplier() + assert(is_equal_approx(upgraded_multiplier, 1.5)) + + var profile := CatchDifficultyProfile.new() + profile.barrier_count_min = 0 + profile.barrier_count_max = 0 + var controller := CatchController.new() + root.add_child(controller) + controller.start_authoritative_encounter( + profile, + Player.BASE_REEL_SPEED, + 1, + 919191, + ) + controller.set_reel_input(true) + controller.call("_update_free_reeling", 1.0) + var base_progress: float = controller.progress + assert(is_equal_approx(base_progress, Player.BASE_REEL_SPEED)) + + controller.reset() + controller.start_authoritative_encounter( + profile, + Player.BASE_REEL_SPEED * upgraded_multiplier, + 1, + 919191, + ) + controller.set_reel_input(true) + controller.call("_update_free_reeling", 1.0) + assert( + is_equal_approx( + controller.progress, + Player.BASE_REEL_SPEED * upgraded_multiplier, + ) + ) + assert(controller.progress > base_progress) + controller.queue_free() + upgrades.queue_free() + + func _validate_catch_round_trip_and_sale() -> void: var fish: FishData = Catalog.get_fish_by_id(&"bluegill") assert(fish != null) @@ -80,7 +243,7 @@ func _validate_catch_round_trip_and_sale() -> void: selector.deterministic_test_seed = 9911 selector.quality_weight_multipliers = [0.0, 0.0, 0.0, 0.0, 1.0] selector.begin_roll() - var fish_catch: FishCatch = selector.create_catch(fish) + var fish_catch: FishCatch = selector.create_catch(fish, [&"worm"]) assert(fish_catch != null) assert(fish_catch.quality == FishQualityType.Tier.SHINY) assert( @@ -242,7 +405,7 @@ func _validate_version_four_migration() -> void: version_four, 4, ) - assert(int(migrated.get("save_version", -1)) == 6) + assert(int(migrated.get("save_version", -1)) == 7) assert(int((migrated["experience"] as Dictionary)["total_experience"]) == 0) assert( is_equal_approx( @@ -259,4 +422,7 @@ func _validate_version_four_migration() -> void: var boring_bit: int = FishQualityType.bit_for(FishQualityType.Tier.BORING) assert(int(masks["bluegill"]) == boring_bit) assert(int(masks["carp"]) == boring_bit) + assert( + PlayerJobService.validate_save_data(migrated.get("jobs", {})) + ) manager.queue_free() diff --git a/tests/fish_showcase_multiplayer_validation.gd b/tests/fish_showcase_multiplayer_validation.gd index 3b1ab54..72f4a59 100644 --- a/tests/fish_showcase_multiplayer_validation.gd +++ b/tests/fish_showcase_multiplayer_validation.gd @@ -35,8 +35,8 @@ func _run_host() -> void: var fish_catch: FishCatch = _add_bluegill(main, player) assert(player.hotbar.assign_fish(1, fish_catch.catch_id)) assert(player.hotbar.select_slot(1)) - assert(service.toggle_selected_fish()) assert(service.is_local_showcase_visible()) + await _wait_for_held_fish_visibility(player, true) assert(session.set_host_open(true)) var remote_peer_id: int = 0 @@ -53,7 +53,7 @@ func _run_host() -> void: ) as PlayerSpawnService var remote_avatar: Player = spawn_service.get_avatar(remote_peer_id) assert(remote_avatar != null) - var remote_display := remote_avatar.get_node("%HeldFishDisplay") as Node3D + var remote_display := remote_avatar.get("_held_fish_display") as Node3D var visible_deadline: int = Time.get_ticks_msec() + 10000 while Time.get_ticks_msec() < visible_deadline and not remote_display.visible: await process_frame @@ -66,7 +66,9 @@ func _run_host() -> void: print("Fish showcase multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -95,29 +97,46 @@ func _run_client() -> void: ) as PlayerSpawnService var host_avatar: Player = spawn_service.get_avatar(1) assert(host_avatar != null) - var host_display := host_avatar.get_node("%HeldFishDisplay") as Node3D + var host_display := host_avatar.get("_held_fish_display") as Node3D var snapshot_deadline: int = Time.get_ticks_msec() + 10000 while Time.get_ticks_msec() < snapshot_deadline and not host_display.visible: await process_frame assert(host_display.visible) var player := main.get("_player") as Player + var animation_player := player.get_node( + "Visuals/CharacterRig/AnimationPlayer" + ) as AnimationPlayer + for animation_name: StringName in [ + &"pocket_idle_idle", + &"pocket_idle_show", + &"pocket_show_show", + &"pocket_show_idle", + ]: + assert(animation_player.has_animation(animation_name)) var service := main.get_node( "%NetworkFishShowcaseService" ) as NetworkFishShowcaseService var fish_catch: FishCatch = _add_bluegill(main, player) assert(player.hotbar.assign_fish(1, fish_catch.catch_id)) assert(player.hotbar.select_slot(1)) - assert(service.toggle_selected_fish()) - assert((player.get_node("%HeldFishDisplay") as Node3D).visible) + assert(service.is_local_showcase_visible()) + await _wait_for_held_fish_visibility(player, true) await create_timer(2.0).timeout assert(service.toggle_selected_fish()) - assert(not (player.get_node("%HeldFishDisplay") as Node3D).visible) - await create_timer(1.0).timeout + var held_display := player.get("_held_fish_display") as Node3D + assert(held_display.visible) + assert(animation_player.current_animation == &"pocket_show_idle") + var pocket_deadline: int = Time.get_ticks_msec() + 5000 + while Time.get_ticks_msec() < pocket_deadline and held_display.visible: + await process_frame + assert(not held_display.visible) print("Fish showcase multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -151,3 +170,18 @@ func _create_initialized_main() -> Node: await process_frame assert(bool(main.get("_application_initialized"))) return main + + +func _wait_for_held_fish_visibility( + player: Player, + expected_visible: bool, +) -> void: + var held_fish_display := player.get("_held_fish_display") as Node3D + assert(held_fish_display != null) + var deadline_msec: int = Time.get_ticks_msec() + 2000 + while ( + held_fish_display.visible != expected_visible + and Time.get_ticks_msec() < deadline_msec + ): + await process_frame + assert(held_fish_display.visible == expected_visible) diff --git a/tests/fishing_audio_validation.gd b/tests/fishing_audio_validation.gd new file mode 100644 index 0000000..02a59eb --- /dev/null +++ b/tests/fishing_audio_validation.gd @@ -0,0 +1,54 @@ +extends SceneTree + +const FishingSpotScene := preload("res://fishing/fishing_spot.tscn") + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var fishing_spot := FishingSpotScene.instantiate() as FishingSpot + root.add_child(fishing_spot) + await process_frame + + var fight_audio := fishing_spot.get_node("%FightAudio") as AudioStreamPlayer + var bobber_audio := fishing_spot.get_node( + "%BobberAudio" + ) as AudioStreamPlayer + assert(bobber_audio != null) + assert(bobber_audio.stream != null) + assert(bobber_audio.bus == &"SFX") + assert(is_equal_approx(bobber_audio.volume_db, -14.0)) + var bobber_stream := bobber_audio.stream as AudioStreamWAV + assert(bobber_stream != null) + assert(bobber_stream.loop_mode == AudioStreamWAV.LOOP_DISABLED) + assert(fight_audio != null) + assert(fight_audio.stream != null) + assert(fight_audio.bus == &"SFX") + assert(is_equal_approx(fight_audio.volume_db, -10.0)) + var fight_stream := fight_audio.stream as AudioStreamWAV + assert(fight_stream != null) + assert(fight_stream.loop_mode == AudioStreamWAV.LOOP_FORWARD) + assert(fight_stream.loop_end > fight_stream.loop_begin) + var reeling_audio := fishing_spot.get_node( + "%ReelingAudio" + ) as AudioStreamPlayer + assert(reeling_audio != null) + assert(reeling_audio.stream != null) + assert(reeling_audio.bus == &"SFX") + assert(is_equal_approx(reeling_audio.volume_db, -10.0)) + var reeling_stream := reeling_audio.stream as AudioStreamWAV + assert(reeling_stream != null) + assert(reeling_stream.loop_mode == AudioStreamWAV.LOOP_FORWARD) + assert(reeling_stream.loop_end > reeling_stream.loop_begin) + + assert(fishing_spot.has_method("_start_fight_audio")) + assert(fishing_spot.has_method("_play_bobber_audio")) + assert(fishing_spot.has_method("_stop_fight_audio")) + assert(fishing_spot.has_method("_start_reeling_audio")) + assert(fishing_spot.has_method("_stop_reeling_audio")) + fishing_spot.free() + await process_frame + print("Fishing audio validation: PASS") + quit() diff --git a/tests/fishing_audio_validation.gd.uid b/tests/fishing_audio_validation.gd.uid new file mode 100644 index 0000000..e9cd6d7 --- /dev/null +++ b/tests/fishing_audio_validation.gd.uid @@ -0,0 +1 @@ +uid://bby8mv7bsjd3w diff --git a/tests/fishing_authority_validation.gd b/tests/fishing_authority_validation.gd index 2a98968..6b0429d 100644 --- a/tests/fishing_authority_validation.gd +++ b/tests/fishing_authority_validation.gd @@ -31,13 +31,22 @@ func _run() -> void: "%NetworkFishingService" ) as NetworkFishingService var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType + var game_ui := main.get_node("UIPresentation/UIViewport/GameUI") + var fishing_status := game_ui.get_node("%StatusLabel") as Label + var fishing_panel := game_ui.get_node("%FishingPanel") as PanelContainer var player := main.get("_player") as Player assert(session.is_host()) assert(not session.is_open_host()) assert(service != null and fishing_spot != null and player != null) assert(player.hotbar.get_selected_item_id() == &"basic_fishing_rod") + var pond := main.get_node( + "TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond" + ) as Node3D + var pond_region := pond.get_node("FishingRegion") as FishableWaterRegion + assert(pond != null and pond_region != null) + var pond_surface_y: float = pond_region.get_surface_height() - player.global_position = Vector3(-0.5, 3.95, 2.1) + player.global_position = pond.global_position + Vector3(8.9, 1.44, 0.0) var visuals := player.get_node("Visuals") as Node3D visuals.rotation.y = PI * 0.5 for _frame: int in 4: @@ -50,8 +59,8 @@ func _run() -> void: fishing_spot.set("_cast_charge", 0.32) fishing_spot.call("_update_cast_charge", 0.0) var aimed_target: Vector3 = fishing_spot.get("_cast_target") - assert(aimed_target.x < -1.35) - assert(is_equal_approx(aimed_target.y, 2.51)) + assert(aimed_target.x < player.global_position.x - 0.85) + assert(is_equal_approx(aimed_target.y, pond_surface_y)) assert(fishing_spot.is_target_fishable(aimed_target)) fishing_spot.call("_confirm_cast") assert(fishing_spot.state == FishingSpotType.FishingState.CASTING) @@ -65,10 +74,13 @@ func _run() -> void: await process_frame assert(fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE) assert(service.has_local_attempt()) + assert(fishing_status.text.is_empty()) + assert(not fishing_status.visible) + assert(not fishing_panel.visible) var attempts: Dictionary = service.get("_attempts") var attempt: NetworkFishingAttempt = attempts.get(session.get_local_peer_id()) assert(attempt != null) - assert(is_equal_approx(attempt.target.y, 2.51)) + assert(is_equal_approx(attempt.target.y, pond_surface_y)) assert(attempt.bobber_position.is_equal_approx(attempt.target)) fishing_spot.set("_withdrawal_input_held", true) @@ -97,8 +109,78 @@ func _run() -> void: assert(player.is_movement_enabled()) assert(not bobber.visible) + # A private host rolls and retains the authoritative catch before the fight + # so its quality selects the barrier band clients receive in snapshots. + fishing_spot.call("_begin_aiming", player) + fishing_spot.set("_cast_charge", 0.32) + fishing_spot.call("_update_cast_charge", 0.0) + fishing_spot.call("_confirm_cast") + var second_wait_deadline: int = Time.get_ticks_msec() + 4000 + while ( + Time.get_ticks_msec() < second_wait_deadline + and fishing_spot.state != FishingSpotType.FishingState.WAITING_FOR_BITE + ): + await process_frame + assert(fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE) + attempts = service.get("_attempts") + attempt = attempts.get(session.get_local_peer_id()) + assert(attempt != null) + service.call("_start_bite", attempt) + await process_frame + assert(attempt.phase == NetworkFishingAttempt.Phase.FIGHTING) + var catalog: FishPool = main.get("fish_catalog") as FishPool + assert(catalog != null) + var fish: FishData = catalog.get_fish_by_id(attempt.fish_id) + var fish_catch := FishCatch.from_network_dict( + attempt.catch_payload, + fish, + ) + assert(fish_catch != null and fish_catch.is_valid()) + var reference_controller := CatchController.new() + root.add_child(reference_controller) + reference_controller.start_authoritative_encounter( + fish.catch_profile, + attempt.reel_speed, + attempt.barrier_damage, + attempt.encounter_seed, + fish_catch.quality, + ) + var quality_barriers: Array = attempt.controller.get("_barriers") + var reference_barriers: Array = reference_controller.get("_barriers") + assert(quality_barriers.size() == reference_barriers.size()) + for barrier_index: int in quality_barriers.size(): + var quality_barrier: RefCounted = quality_barriers[barrier_index] + var reference_barrier: RefCounted = reference_barriers[barrier_index] + assert( + is_equal_approx( + float(quality_barrier.get("position")), + float(reference_barrier.get("position")), + ) + ) + assert( + int(quality_barrier.get("maximum_health")) + == int(reference_barrier.get("maximum_health")) + ) + assert( + int(quality_barrier.get("maximum_health")) + >= FishQuality.BARRIER_HEALTH_MINIMUMS[fish_catch.quality] + ) + assert( + int(quality_barrier.get("maximum_health")) + <= FishQuality.BARRIER_HEALTH_MAXIMUMS[fish_catch.quality] + ) + reference_controller.queue_free() + service.call("_on_attempt_escaped", session.get_local_peer_id()) + await process_frame + assert(not service.has_local_attempt()) + assert(fishing_status.text.is_empty()) + assert(not fishing_status.visible) + assert(not fishing_panel.visible) + print("Fishing authority validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() diff --git a/tests/fishing_multiplayer_validation.gd b/tests/fishing_multiplayer_validation.gd index fe9db15..c40e492 100644 --- a/tests/fishing_multiplayer_validation.gd +++ b/tests/fishing_multiplayer_validation.gd @@ -32,7 +32,7 @@ func _run_host() -> void: "%NetworkFishingService" ) as NetworkFishingService - var join_deadline: int = Time.get_ticks_msec() + 20000 + var join_deadline: int = Time.get_ticks_msec() + 60000 while ( Time.get_ticks_msec() < join_deadline and session.get_authenticated_peer_ids().size() < 2 @@ -50,10 +50,35 @@ func _run_host() -> void: ) as PlayerSpawnService var remote_avatar: Player = spawn_service.get_avatar(remote_peer_id) assert(remote_avatar != null) + assert(remote_avatar.bag.get_quantity(&"worms") == 0) remote_avatar.global_position = Vector3(-0.5, 3.95, 2.1) var remote_visuals := remote_avatar.get_node("Visuals") as Node3D remote_visuals.rotation.y = PI * 0.5 session.publish_authoritative_teleport(remote_peer_id) + var aiming_deadline: int = Time.get_ticks_msec() + 15000 + while ( + Time.get_ticks_msec() < aiming_deadline + and int(remote_avatar.get("_fishing_visual_phase")) + != Player.FishingVisualPhase.CASTING + ): + await process_frame + assert( + int(remote_avatar.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.CASTING + ) + var remote_position_error := ( + remote_avatar.global_position - Vector3(-0.5, 3.95, 2.1) + ) + remote_position_error.y = 0.0 + assert( + remote_position_error.length() <= 0.25, + "remote casting position drifted: %s" % remote_avatar.global_position + ) + assert( + remote_avatar.get_facing_direction().dot(Vector3.LEFT) > 0.99, + "remote casting facing drifted: %s" + % remote_avatar.get_facing_direction() + ) var saw_remote_attempt: bool = false var remote_presentation: RemoteFishingPresentation @@ -65,8 +90,8 @@ func _run_host() -> void: if peer_id == session.get_local_peer_id(): continue var attempt: NetworkFishingAttempt = attempts[peer_id] - assert(is_equal_approx(attempt.target.y, 2.51)) - assert(attempt.bobber_position.y <= 2.51 + 0.001) + assert(attempt.target.is_finite()) + assert(attempt.bobber_position.distance_to(attempt.target) <= 0.001) var presentations: Dictionary = service.get("_remote_presentations") remote_presentation = presentations.get(peer_id) assert(remote_presentation != null) @@ -78,6 +103,31 @@ func _run_host() -> void: if saw_remote_attempt: break assert(saw_remote_attempt) + assert( + int(remote_avatar.get("_fishing_visual_phase")) + in [ + Player.FishingVisualPhase.RELEASE, + Player.FishingVisualPhase.FISHING, + ] + ) + var sitting_deadline: int = Time.get_ticks_msec() + 10000 + while Time.get_ticks_msec() < sitting_deadline and not remote_avatar.is_sitting(): + await process_frame + assert(remote_avatar.is_sitting()) + var fishing_deadline: int = Time.get_ticks_msec() + 5000 + while ( + Time.get_ticks_msec() < fishing_deadline + and int(remote_avatar.get("_fishing_visual_phase")) + != Player.FishingVisualPhase.FISHING + ): + await process_frame + assert( + int(remote_avatar.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.FISHING + ) + var remote_animation_player := remote_avatar.get_node( + "Visuals/CharacterRig/AnimationPlayer" + ) as AnimationPlayer var return_deadline: int = Time.get_ticks_msec() + 5000 while ( Time.get_ticks_msec() < return_deadline @@ -85,19 +135,24 @@ func _run_host() -> void: ): await process_frame assert(not service.has_peer_attempt(remote_peer_id)) - assert(is_instance_valid(remote_presentation)) - assert(remote_presentation.get("_return_tween") != null) - - var completion_deadline: int = Time.get_ticks_msec() + 12000 + assert( + int(remote_avatar.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.RETRACT + ) + var retract_animation_deadline: int = Time.get_ticks_msec() + 3000 while ( - Time.get_ticks_msec() < completion_deadline - and session.get_authenticated_peer_ids().size() == 2 + Time.get_ticks_msec() < retract_animation_deadline + and remote_animation_player.current_animation != &"retract_sit" ): await process_frame + assert(remote_animation_player.current_animation == &"retract_sit") + print("Fishing multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -119,8 +174,23 @@ func _run_client() -> void: var service := main.get_node( "%NetworkFishingService" ) as NetworkFishingService + var cast_rejections: Array[String] = [] + service.local_cast_rejected.connect( + func(message: String) -> void: + cast_rejections.append(message) + ) var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType var player := main.get("_player") as Player + var item_catalog := main.get("item_catalog") as ItemCatalog + var worms: ItemData = item_catalog.get_item_by_id(&"worms") + if player.bag.get_quantity(&"worms") == 0: + assert(player.bag.add_item(&"worms", 1)) + assert(player.equip_bait(worms)) + var worms_before_cast: int = player.bag.get_quantity(&"worms") + var character_animation_player := player.get_node( + "Visuals/CharacterRig/AnimationPlayer" + ) as AnimationPlayer + assert(character_animation_player.has_animation(&"retract_sit")) var placement_deadline: int = Time.get_ticks_msec() + 5000 while ( Time.get_ticks_msec() < placement_deadline @@ -131,14 +201,59 @@ func _run_client() -> void: assert(player.global_position.distance_to(Vector3(-0.5, 3.95, 2.1)) <= 0.25) assert(player.get_facing_direction().dot(Vector3.LEFT) > 0.99) + player.set_casting_visual() + assert( + int(player.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.CASTING + ) + for _frame: int in 30: + await physics_frame + player.set_fishing_visual(false) + player.call("_set_sitting", true, true) + assert(player.is_sitting()) + for _frame: int in 15: + await physics_frame fishing_spot.call("_begin_aiming", player) assert(player.is_movement_enabled()) - fishing_spot.set("_cast_charge", 0.32) - fishing_spot.call("_update_cast_charge", 0.0) - var target: Vector3 = fishing_spot.get("_cast_target") - assert(fishing_spot.is_target_fishable(target)) - assert(is_equal_approx(target.y, 2.51)) - fishing_spot.call("_confirm_cast") + assert( + int(player.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.CASTING + ) + var cast_charge: float = 0.0 + var target: Vector3 = Vector3.ZERO + var cast_origin: Vector3 = fishing_spot.get("_cast_origin_position") + for charge_step: int in range(1, 21): + var candidate_charge := float(charge_step) / 20.0 + fishing_spot.set("_cast_charge", candidate_charge) + fishing_spot.call("_update_cast_charge", 0.0) + var candidate_target: Vector3 = fishing_spot.get("_cast_target") + var candidate_surface := fishing_spot.get( + "_aim_surface_sample" + ) as FishingSurfaceSample + if ( + candidate_surface.is_fishable() + and fishing_spot.is_cast_path_clear(cast_origin, candidate_target) + ): + cast_charge = candidate_charge + target = candidate_target + break + assert(cast_charge > 0.0, "no clear fishable test target was found") + var evidence: Dictionary = fishing_spot.call("_build_network_evidence") + fishing_spot.set("state", FishingSpotType.FishingState.CASTING) + player.set_movement_enabled(false) + player.set_release_visual() + assert( + int(player.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.RELEASE + ) + assert( + not service.request_local_cast( + cast_origin, + target, + cast_charge, + evidence, + ).is_empty() + ) var accepted_deadline: int = Time.get_ticks_msec() + 6000 while ( @@ -146,8 +261,33 @@ func _run_client() -> void: and fishing_spot.state != FishingSpotType.FishingState.WAITING_FOR_BITE ): await process_frame - assert(fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE) + assert( + fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE, + ( + "cast acceptance timed out: state=%s local_attempt=%s target=%s " + + "rejections=%s" + ) % [ + fishing_spot.state, + service.has_local_attempt(), + target, + cast_rejections, + ] + ) assert(service.has_local_attempt()) + assert(player.bag.get_quantity(&"worms") == worms_before_cast - 1) + var fishing_deadline: int = Time.get_ticks_msec() + 5000 + while ( + Time.get_ticks_msec() < fishing_deadline + and int(player.get("_fishing_visual_phase")) + != Player.FishingVisualPhase.FISHING + ): + await process_frame + assert( + int(player.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.FISHING + ) + for _frame: int in 30: + await physics_frame fishing_spot.set("_withdrawal_input_held", true) fishing_spot.set("_network_primary_input_held", true) service.submit_local_input(true, true) @@ -159,10 +299,24 @@ func _run_client() -> void: ): await process_frame assert(fishing_spot.state == FishingSpotType.FishingState.RETURNING) + assert( + int(player.get("_fishing_visual_phase")) + == Player.FishingVisualPhase.RETRACT + ) + var retract_animation_deadline: int = Time.get_ticks_msec() + 3000 + while ( + Time.get_ticks_msec() < retract_animation_deadline + and character_animation_player.current_animation != &"retract_sit" + ): + await process_frame + assert(character_animation_player.current_animation == &"retract_sit") var bobber := fishing_spot.get_node( "FishingPresentation/Bobber" ) as MeshInstance3D assert(bobber.visible) + while not player.is_retract_visual_complete(): + assert(fishing_spot.state == FishingSpotType.FishingState.RETURNING) + await process_frame var ready_deadline: int = Time.get_ticks_msec() + 3000 while ( @@ -173,10 +327,22 @@ func _run_client() -> void: assert(fishing_spot.state == FishingSpotType.FishingState.READY) assert(player.is_movement_enabled()) assert(not bobber.visible) + var host_observation_deadline: int = Time.get_ticks_msec() + 2000 + while Time.get_ticks_msec() < host_observation_deadline: + await process_frame print("Fishing multiplayer client validation: PASS") - session.disconnect_session("") + var host_completion_deadline: int = Time.get_ticks_msec() + 10000 + while ( + Time.get_ticks_msec() < host_completion_deadline + and session.is_joined_client() + ): + await process_frame + if session.is_joined_client(): + session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() diff --git a/tests/fishing_surface_validation.gd b/tests/fishing_surface_validation.gd index c178aac..5b52b41 100644 --- a/tests/fishing_surface_validation.gd +++ b/tests/fishing_surface_validation.gd @@ -50,6 +50,7 @@ func _validate_resolver_layers() -> void: _add_solid_box(world, Vector3(40.0, 2.0, 12.0), Vector3(10.0, -1.0, 0.0)) _add_water_region(world, Vector3(0.0, 2.0, 0.0), Vector2(8.0, 8.0), PondPool) _add_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(2.0, 2.25, 0.0)) + _add_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(-2.0, 6.0, 0.0)) _add_water_region(world, Vector3(10.0, 5.0, 0.0), Vector2(6.0, 6.0), PondPool) _add_water_region(world, Vector3(20.0, 3.0, 0.0), Vector2(6.0, 6.0), null) await physics_frame @@ -64,6 +65,14 @@ func _validate_resolver_layers() -> void: ) assert(water.is_fishable()) assert(is_equal_approx(water.position.y, 2.0)) + var water_under_overhang: FishingSurfaceSampleType = resolver.resolve_surface( + space_state, + Vector3(-2.0, 4.0, 0.0), + 4.0, + 0.08, + ) + assert(water_under_overhang.is_fishable()) + assert(is_equal_approx(water_under_overhang.position.y, 2.0)) var covered_water: FishingSurfaceSampleType = resolver.resolve_surface( space_state, @@ -153,6 +162,34 @@ func _validate_portable_water_body() -> void: func _validate_starter_region_surfaces() -> void: var region := StarterRegionScene.instantiate() as Node3D root.add_child(region) + var ocean_region := region.get_node( + "WaterBodies/Ocean/FishingRegions/OceanFishingRegion" + ) as FishableWaterRegionType + var minimum_x: float = INF + var maximum_x: float = -INF + var minimum_z: float = INF + var maximum_z: float = -INF + for child: Node in ocean_region.get_children(): + var shape_node := child as CollisionShape3D + assert(shape_node != null and shape_node.shape is BoxShape3D) + var box := shape_node.shape as BoxShape3D + assert(is_equal_approx(box.size.y, 2.0)) + minimum_x = minf(minimum_x, shape_node.position.x - box.size.x * 0.5) + maximum_x = maxf(maximum_x, shape_node.position.x + box.size.x * 0.5) + minimum_z = minf(minimum_z, shape_node.position.z - box.size.z * 0.5) + maximum_z = maxf(maximum_z, shape_node.position.z + box.size.z * 0.5) + var ocean_footprint := Vector2( + maximum_x - minimum_x, + maximum_z - minimum_z, + ) + assert(is_equal_approx( + ocean_footprint.x * ocean_footprint.y, + 12840.0, + )) + assert(is_equal_approx( + ocean_footprint.x / 107.0, + ocean_footprint.y / 80.0, + )) var fishing_spot := FishingSpotScene.instantiate() as FishingSpotType root.add_child(fishing_spot) await physics_frame @@ -170,6 +207,14 @@ func _validate_starter_region_surfaces() -> void: ) assert(ocean.is_fishable()) assert(is_equal_approx(ocean.position.y, -0.45)) + var expanded_ocean: FishingSurfaceSampleType = ( + fishing_spot.resolve_fishing_surface( + Vector3(72.0, -0.45, 0.0), + 4.0, + ) + ) + assert(expanded_ocean.is_fishable()) + assert(is_equal_approx(expanded_ocean.position.y, -0.45)) var covered_ocean: FishingSurfaceSampleType = ( fishing_spot.resolve_fishing_surface( Vector3(20.0, -0.45, 0.0), @@ -180,6 +225,27 @@ func _validate_starter_region_surfaces() -> void: assert(not covered_ocean.is_fishable()) assert(covered_ocean.position.y > -0.45) + # The ocean fishing layer covers the island's complete editable footprint. + # Terrain at or above the waterline remains authoritative, while shoreline + # terrain lowered below the surface becomes fishable without reshaping a + # manually authored exclusion ring. + var editable_shallows_found := 0 + for x_position: int in range(-24, 24, 2): + for z_position: int in range(-21, 22, 2): + var candidate: FishingSurfaceSampleType = ( + fishing_spot.resolve_fishing_surface( + Vector3(x_position, -0.45, z_position), + 4.0, + ) + ) + if ( + candidate.is_fishable() + and candidate.water_region.water_type + == WaterType.Type.SALT_WATER + ): + editable_shallows_found += 1 + assert(editable_shallows_found > 0) + fishing_spot.queue_free() region.queue_free() await process_frame @@ -247,21 +313,57 @@ func _validate_bite_wait_distribution() -> void: var total_wait_seconds: float = 0.0 for _sample_index: int in 10000: var wait_seconds: float = fishing_spot.roll_bite_wait_time() - assert(wait_seconds >= 10.0) - assert(wait_seconds <= 240.0) + assert(wait_seconds >= FishingSpotType.BITE_QUICK_MIN_SECONDS) + assert(wait_seconds <= FishingSpotType.BITE_MAX_SECONDS) total_wait_seconds += wait_seconds - if wait_seconds < 30.0: + if wait_seconds < FishingSpotType.BITE_QUICK_MAX_SECONDS: quick_count += 1 else: typical_or_long_count += 1 - if wait_seconds >= 180.0: + if wait_seconds >= FishingSpotType.BITE_LONG_MAX_SECONDS: very_long_count += 1 var average_wait_seconds: float = total_wait_seconds / 10000.0 - assert(quick_count >= 2300 and quick_count <= 2700) + var quick_ratio: float = float(quick_count) / 10000.0 + var very_long_ratio: float = float(very_long_count) / 10000.0 + var very_long_probability: float = 1.0 - ( + FishingSpotType.BITE_QUICK_PROBABILITY + + FishingSpotType.BITE_TYPICAL_PROBABILITY + + FishingSpotType.BITE_LONG_PROBABILITY + ) + var expected_average_wait_seconds: float = ( + FishingSpotType.BITE_QUICK_PROBABILITY + * ( + FishingSpotType.BITE_QUICK_MIN_SECONDS + + FishingSpotType.BITE_QUICK_MAX_SECONDS + ) + * 0.5 + + FishingSpotType.BITE_TYPICAL_PROBABILITY + * ( + FishingSpotType.BITE_QUICK_MAX_SECONDS + + FishingSpotType.BITE_TYPICAL_MAX_SECONDS + ) + * 0.5 + + FishingSpotType.BITE_LONG_PROBABILITY + * ( + FishingSpotType.BITE_TYPICAL_MAX_SECONDS + + FishingSpotType.BITE_LONG_MAX_SECONDS + ) + * 0.5 + + very_long_probability + * ( + FishingSpotType.BITE_LONG_MAX_SECONDS + + FishingSpotType.BITE_MAX_SECONDS + ) + * 0.5 + ) + assert(absf( + quick_ratio - FishingSpotType.BITE_QUICK_PROBABILITY + ) <= 0.02) assert(typical_or_long_count > quick_count) - assert(very_long_count >= 100 and very_long_count <= 300) - assert(average_wait_seconds >= 57.0) - assert(average_wait_seconds <= 61.0) + assert(absf(very_long_ratio - very_long_probability) <= 0.005) + assert(absf( + average_wait_seconds - expected_average_wait_seconds + ) <= 1.0) fishing_spot.queue_free() await process_frame @@ -296,8 +398,16 @@ func _validate_remote_presentation() -> void: fish_catch.sale_value = 1 assert(fish_catch.is_valid()) presentation.play_return(fish_catch) - await create_timer(0.6).timeout - var catch_display := player.get_node("%CatchDisplay") as Node3D + var catch_display := player.find_child( + "CatchDisplay", true, false + ) as Node3D + assert(catch_display != null) + var showcase_deadline_msec: int = Time.get_ticks_msec() + 2000 + while ( + not catch_display.visible + and Time.get_ticks_msec() < showcase_deadline_msec + ): + await process_frame assert(catch_display.visible) await presentation.return_completed assert(not catch_display.visible) diff --git a/tests/fur_pattern_validation.gd b/tests/fur_pattern_validation.gd new file mode 100644 index 0000000..0bd3eb2 --- /dev/null +++ b/tests/fur_pattern_validation.gd @@ -0,0 +1,192 @@ +extends SceneTree + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var legacy_snapshot := CharacterCustomizationCatalog.default_snapshot() + for added_id: String in [ + "fur_style", + "fur_color_2", + "fur_color_3", + "fur_color_4", + ]: + legacy_snapshot.erase(added_id) + var migrated := CharacterCustomizationCatalog.sanitized_snapshot( + legacy_snapshot + ) + assert(CharacterCustomizationCatalog.validate_snapshot(migrated)) + assert(migrated["fur_style"] == "solid") + var fur_colors := CharacterCustomizationCatalog.options_for("fur_pattern") + assert(fur_colors.size() == 30) + for natural_color_id: String in [ + "ivory", + "sand", + "tan", + "taupe", + "black", + "dark_brown", + "chocolate", + "chestnut", + "ginger", + "sage", + "forest", + ]: + assert(CharacterCustomizationCatalog.is_valid_option( + "fur_pattern", + natural_color_id, + )) + + var pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "spots_bengal" + ) + assert(pattern_texture != null) + assert(pattern_texture.get_width() == 1024) + assert(pattern_texture.get_height() == 1024) + var arm_pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "spots_bengal", + "body_arms", + ) + assert(arm_pattern_texture != null) + assert(arm_pattern_texture.get_width() == 1024) + assert(arm_pattern_texture.get_height() == 1024) + var fox_pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "fox", + "body_main", + ) + assert(fox_pattern_texture != null) + assert(fox_pattern_texture.get_width() == 1024) + assert(fox_pattern_texture.get_height() == 1024) + var fox_arm_pattern_texture := ( + CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "body_arms" + ) + ) + assert(fox_arm_pattern_texture != null) + assert(fox_arm_pattern_texture.get_width() == 1024) + assert(fox_arm_pattern_texture.get_height() == 1024) + var bengal_head_texture := ( + CharacterCustomizationCatalog.fur_pattern_texture( + "spots_bengal", "head_round" + ) + ) + assert(bengal_head_texture != null) + assert(bengal_head_texture.get_width() == 1024) + assert(bengal_head_texture.get_height() == 1024) + var fox_head_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "head_pointy" + ) + assert(fox_head_texture != null) + assert(fox_head_texture.get_width() == 1024) + assert(fox_head_texture.get_height() == 1024) + var fox_round_head_texture := ( + CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "head_round" + ) + ) + assert(fox_round_head_texture != null) + assert(fox_round_head_texture.get_width() == 1024) + assert(fox_round_head_texture.get_height() == 1024) + var fox_tail_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "tails_fox" + ) + assert(fox_tail_texture != null) + assert(fox_tail_texture.get_width() == 1024) + assert(fox_tail_texture.get_height() == 1024) + + var patterned := migrated.duplicate(true) + patterned["fur_pattern"] = "orange" + patterned["fur_style"] = "spots_bengal" + patterned["fur_color_2"] = "cream" + patterned["fur_color_3"] = "brown" + patterned["fur_color_4"] = "red" + assert(CharacterCustomizationCatalog.validate_snapshot(patterned)) + + var visuals := PlayerVisualPresenter.instantiate_visuals() + root.add_child(visuals) + PlayerVisualPresenter.apply_appearance(visuals, patterned) + var skeleton := visuals.find_child("Skeleton3D", true, false) as Skeleton3D + assert(skeleton != null) + var body := skeleton.get_node_or_null("body_main") as MeshInstance3D + assert(body != null) + var body_arrays := body.mesh.surface_get_arrays(0) + var body_uvs := body_arrays[Mesh.ARRAY_TEX_UV] as PackedVector2Array + assert(not body_uvs.is_empty()) + var uv_min := Vector2(INF, INF) + var uv_max := Vector2(-INF, -INF) + for uv: Vector2 in body_uvs: + uv_min = uv_min.min(uv) + uv_max = uv_max.max(uv) + assert(uv_min.x < 0.05 and uv_min.y < 0.05) + assert(uv_max.x > 0.95 and uv_max.y > 0.95) + var material := body.material_override as ShaderMaterial + assert(material != null) + assert(material.shader == preload("res://player/fur_pattern.gdshader")) + assert(material.get_shader_parameter("pattern_texture") == pattern_texture) + assert(material.get_shader_parameter("base_color") == Color("c86c36")) + assert(material.get_shader_parameter("red_zone_color") == Color("e6d39b")) + assert(material.get_shader_parameter("green_zone_color") == Color("7b4a32")) + assert(material.get_shader_parameter("blue_zone_color") == Color("a9433f")) + var arms := skeleton.get_node_or_null("body_arms") as MeshInstance3D + assert(arms != null) + var arm_material := arms.material_override as ShaderMaterial + assert(arm_material != null) + assert( + arm_material.get_shader_parameter("pattern_texture") + == arm_pattern_texture + ) + var round_head := skeleton.get_node_or_null("head_round") as MeshInstance3D + assert(round_head != null) + var round_head_material := round_head.material_override as ShaderMaterial + assert(round_head_material != null) + assert( + round_head_material.get_shader_parameter("pattern_texture") + == bengal_head_texture + ) + + var fox_snapshot := patterned.duplicate(true) + fox_snapshot["species"] = "pointy" + fox_snapshot["fur_style"] = "fox" + fox_snapshot["tail"] = "fox" + PlayerVisualPresenter.apply_appearance(visuals, fox_snapshot) + var pointy_head := ( + skeleton.get_node_or_null("head_pointy") as MeshInstance3D + ) + assert(pointy_head != null) + var pointy_head_material := pointy_head.material_override as ShaderMaterial + assert(pointy_head_material != null) + assert( + pointy_head_material.get_shader_parameter("pattern_texture") + == fox_head_texture + ) + fox_snapshot["species"] = "round" + PlayerVisualPresenter.apply_appearance(visuals, fox_snapshot) + assert( + round_head_material.get_shader_parameter("pattern_texture") + == fox_round_head_texture + ) + var fox_tail := skeleton.get_node_or_null("tails_fox") as MeshInstance3D + assert(fox_tail != null) + var fox_tail_material := fox_tail.material_override as ShaderMaterial + assert(fox_tail_material != null) + assert( + fox_tail_material.get_shader_parameter("pattern_texture") + == fox_tail_texture + ) + + var recolored := patterned.duplicate(true) + recolored["fur_color_3"] = "teal" + assert( + NetworkProfileProtocol.signature_fields({"appearance": patterned}) + != NetworkProfileProtocol.signature_fields({"appearance": recolored}) + ) + + PlayerVisualPresenter.apply_appearance(visuals, migrated) + assert(body.material_override is StandardMaterial3D) + + print("Fur pattern validation: PASS") + visuals.queue_free() + await process_frame + quit() diff --git a/tests/fur_pattern_validation.gd.uid b/tests/fur_pattern_validation.gd.uid new file mode 100644 index 0000000..7333f9d --- /dev/null +++ b/tests/fur_pattern_validation.gd.uid @@ -0,0 +1 @@ +uid://bwdmyn34biahx diff --git a/tests/inventory_notepad_art_validation.gd b/tests/inventory_notepad_art_validation.gd index cbe3414..de76221 100644 --- a/tests/inventory_notepad_art_validation.gd +++ b/tests/inventory_notepad_art_validation.gd @@ -58,11 +58,14 @@ func _run() -> void: )) _validate_shared_inventory_geometry(player_menu) _validate_inventory_layering(player_menu) + _validate_cooler_notepad_typography(player_menu) _validate_resolution_matrix() await _capture_inventory_pages(player_menu) print("Inventory notepad artwork validation: PASS") presentation_stage.queue_free() - await process_frame + for _frame: int in 10: + await process_frame + await create_timer(0.1).timeout quit() @@ -126,7 +129,7 @@ func _validate_inventory_layering(player_menu: PlayerMenu) -> void: # The contextual Hotbar uses z=90 while the Player Menu is open. assert(sale_confirmation.z_index > 90) assert(sale_confirmation.position.is_equal_approx(Vector2(380.0, 265.0))) - assert(sale_confirmation.size.is_equal_approx(Vector2(520.0, 190.0))) + assert(sale_confirmation.size.is_equal_approx(Vector2(520.0, 200.0))) var confirmation_style := sale_confirmation.get_theme_stylebox( "panel" ) as StyleBoxFlat @@ -138,6 +141,39 @@ func _validate_inventory_layering(player_menu: PlayerMenu) -> void: ) +func _validate_cooler_notepad_typography(player_menu: PlayerMenu) -> void: + var sort_choice := player_menu.get_node("%CoolerSortOption") as Control + var sort_direction := player_menu.get_node( + "%CoolerSortDirection" + ) as Button + var empty_selection := player_menu.get_node( + "%CoolerSelectionEmpty" + ) as Label + var favorite := player_menu.get_node("%FavoriteBubble") as Button + var sell := player_menu.get_node("%SellBubble") as Button + var sell_all := player_menu.get_node("%SellAllBubble") as Button + assert(sort_choice != null) + assert(sort_direction != null) + assert(empty_selection != null) + assert(favorite != null and sell != null and sell_all != null) + var displayed_value := sort_choice.get_node("%DisplayedValue") as Label + assert(displayed_value != null) + assert(displayed_value.get_theme_font_size("font_size") == 17) + for choice_name: StringName in [ + &"CatchOrderChoice", + &"NameChoice", + &"RarityChoice", + ]: + var choice := sort_choice.get_node("%%%s" % choice_name) as Button + assert(choice != null) + assert(choice.get_theme_font_size("font_size") == 17) + assert(sort_direction.get_theme_font_size("font_size") == 17) + assert(empty_selection.get_theme_font_size("font_size") == 20) + assert(favorite.get_theme_font_size("font_size") == 19) + assert(sell.get_theme_font_size("font_size") == 19) + assert(sell_all.get_theme_font_size("font_size") == 17) + + func _validate_resolution_matrix() -> void: for target_size: Vector2 in TARGET_SIZES: var stage_rect: Rect2 = UIReferencePresentationType.get_rect( diff --git a/tests/job_multiplayer_validation.gd b/tests/job_multiplayer_validation.gd new file mode 100644 index 0000000..c31d392 --- /dev/null +++ b/tests/job_multiplayer_validation.gd @@ -0,0 +1,128 @@ +extends SceneTree + +const MainScene: PackedScene = preload("res://main/main.tscn") +const TEST_PORT: int = 18139 + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var arguments: PackedStringArray = OS.get_cmdline_user_args() + if arguments.has("host"): + await _run_host() + return + if arguments.has("client"): + await _run_client() + return + push_error("Job multiplayer validation needs host or client mode.") + quit(1) + + +func _run_host() -> void: + var main: Node = await _create_initialized_main() + var session := main.get_node("%NetworkSession") as NetworkSession + var jobs := main.get_node("%PlayerJobService") as PlayerJobService + assert(session.start_dedicated_host(TEST_PORT, 8, "127.0.0.1")) + jobs.begin_progression_session() + await process_frame + assert(session.set_host_open(true)) + assert(session.is_dedicated_host()) + var host_board: Dictionary = jobs.get_host_board_network_data() + assert(PlayerJobService.validate_board(host_board)) + + var remote_peer_id: int = 0 + var join_deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < join_deadline and remote_peer_id == 0: + await process_frame + for peer_id: int in session.get_authenticated_peer_ids(): + if peer_id != session.get_local_peer_id(): + remote_peer_id = peer_id + break + assert(remote_peer_id > 1) + assert(session.peer_supports_capability( + remote_peer_id, NetworkProtocol.JOBS_CAPABILITY + )) + + var disconnect_deadline: int = Time.get_ticks_msec() + 12000 + while ( + Time.get_ticks_msec() < disconnect_deadline + and session.is_authenticated_peer(remote_peer_id) + ): + await process_frame + assert(not session.is_authenticated_peer(remote_peer_id)) + print("Job multiplayer host validation: PASS") + session.disconnect_session("") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _run_client() -> void: + var main: Node = await _create_initialized_main() + main.call( + "_on_title_join_game_requested", + "127.0.0.1:%d" % TEST_PORT, + ) + var session := main.get_node("%NetworkSession") as NetworkSession + var jobs := main.get_node("%PlayerJobService") as PlayerJobService + var weather := main.get_node("%WorldWeatherService") as WorldWeatherService + var join_deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < join_deadline: + await process_frame + if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY: + main.call("_confirm_server_trust") + if ( + session.is_joined_client() + and bool(main.get("_gameplay_started")) + and not jobs.get_plan_id().is_empty() + ): + break + assert(session.is_joined_client()) + assert(session.supports_server_capability(NetworkProtocol.JOBS_CAPABILITY)) + assert(not jobs.get_plan_id().is_empty()) + assert(jobs.get_daily_jobs().size() == JobCatalog.DAILY_JOB_COUNT) + assert(jobs.get_forecast().size() == JobCatalog.WEATHER_SEGMENT_COUNT) + var preserved_plan_id: String = jobs.get_plan_id() + jobs.clear_remote_board() + assert(not jobs.has_active_board()) + assert(jobs.get_plan_id() == preserved_plan_id) + var retry_deadline: int = Time.get_ticks_msec() + 5000 + while ( + Time.get_ticks_msec() < retry_deadline + and not jobs.has_active_board() + ): + await process_frame + assert(jobs.has_active_board()) + assert(jobs.get_plan_id() == preserved_plan_id) + assert(jobs.get_daily_jobs().size() == JobCatalog.DAILY_JOB_COUNT) + assert(jobs.get_forecast().size() == JobCatalog.WEATHER_SEGMENT_COUNT) + assert(weather.get_daily_plan_id().is_empty()) + var game_ui := main.get_node("%GameUI") as GameUI + var player_menu := game_ui.get("_player_menu") as PlayerMenu + player_menu.call("_show_section_immediate", PlayerMenu.Section.NET) + assert((player_menu.get_node("%TheNetPage") as TheNetPage).visible) + print("Job multiplayer client validation: PASS") + session.disconnect_session("") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _create_initialized_main() -> Node: + root.size = Vector2i(1280, 720) + var main: Node = MainScene.instantiate() + root.add_child(main) + for _frame: int in 4: + await process_frame + if not bool(main.get("_application_initialized")): + main.call("_activate_selected_data_path", "", true) + for _frame: int in 8: + await process_frame + assert(bool(main.get("_application_initialized"))) + return main diff --git a/tests/job_multiplayer_validation.gd.uid b/tests/job_multiplayer_validation.gd.uid new file mode 100644 index 0000000..1b526ba --- /dev/null +++ b/tests/job_multiplayer_validation.gd.uid @@ -0,0 +1 @@ +uid://c4y0pmxk2bhmu diff --git a/tests/job_system_validation.gd b/tests/job_system_validation.gd new file mode 100644 index 0000000..32fe10e --- /dev/null +++ b/tests/job_system_validation.gd @@ -0,0 +1,329 @@ +extends SceneTree + +const MainScene: PackedScene = preload("res://main/main.tscn") +const Catalog: FishPool = preload("res://fish/pools/fish_catalog.tres") + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + root.size = Vector2i(1280, 720) + var main: Node = MainScene.instantiate() + root.add_child(main) + for _frame: int in 4: + await process_frame + if not bool(main.get("_application_initialized")): + main.call("_activate_selected_data_path", "", true) + for _frame: int in 8: + await process_frame + assert(bool(main.get("_application_initialized"))) + var session := main.get_node("%NetworkSession") as NetworkSession + assert(session != null) + var occupied_port := PacketPeerUDP.new() + assert(occupied_port.bind(18137, "127.0.0.1") == OK) + assert(session.start_private_host(18137, 3)) + assert(session.get_host_port() == 18138) + occupied_port.close() + var save_manager := main.get("_save_manager") as PlayerSaveManager + assert(save_manager.initialize_new_game()) + main.call("_enter_gameplay") + await physics_frame + await physics_frame + + var jobs := main.get_node("%PlayerJobService") as PlayerJobService + var weather := main.get_node("%WorldWeatherService") as WorldWeatherService + var network_fishing := main.get( + "_network_fishing" + ) as NetworkFishingService + var network_sale := main.get("_network_sale") as NetworkSaleService + var player := main.get("_player") as Player + assert(jobs != null and weather != null and player != null) + assert(network_fishing != null and network_sale != null) + + var board: Dictionary = jobs.get_host_board_network_data() + assert(PlayerJobService.validate_board(board)) + assert(jobs.get_daily_jobs().size() == JobCatalog.DAILY_JOB_COUNT) + assert(weather.get_daily_plan_id() == jobs.get_plan_id()) + _validate_weather_guarantees(board) + _validate_late_board_weather_fallback() + _validate_remote_tamper_rejection(jobs, board) + + var sell_job: Dictionary = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.SELL_TOTAL + ) + assert(not sell_job.is_empty()) + var sell_target: int = int(sell_job.get("target", 0)) + var sold_ids: Array[StringName] = [] + for index: int in sell_target * 2: + sold_ids.append(StringName("job-sale-%d" % index)) + network_sale.local_sale_finished.emit( + "job-test", true, "sold", sold_ids, 1 + ) + await process_frame + var pending: Array[Dictionary] = jobs.get_pending_rewards() + assert(pending.size() == 1) + var refreshed_sell: Dictionary = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.SELL_TOTAL + ) + assert(int(refreshed_sell.get("progress", -1)) == sell_target) + assert(int(refreshed_sell.get("completed_count", 0)) == 1) + + var game_ui := main.get_node("%GameUI") as GameUI + var player_menu := game_ui.get("_player_menu") as PlayerMenu + assert(player_menu != null) + player_menu.open_menu() + for _frame: int in 24: + await process_frame + assert(player_menu.visible) + player_menu.call("_show_section_immediate", PlayerMenu.Section.NET) + var net_page := player_menu.get_node("%TheNetPage") as TheNetPage + assert(net_page.visible) + assert((player_menu.get_node("%TheNetTab") as Button).button_pressed) + assert( + (player_menu.get_node("%NavigationCluster") as Control).get_child_count() + == 7 + ) + await _validate_unavailable_fishnet_layout() + + var wallet_before: int = player.wallet.get_balance() + var experience_before: int = player.experience.get_total_experience() + var first_claim_id: String = str(pending[0].get("claim_id", "")) + net_page.call("_claim", first_claim_id) + await process_frame + await process_frame + assert(bool(game_ui.get("_experience_animation_active"))) + var experience_presentation := ( + game_ui.get_node("%ExperiencePresentation") as Control + ) + assert(experience_presentation.visible) + assert((game_ui.get_node("%ExperienceProgressPanel") as Control).visible) + assert((game_ui.get_node("%ExperienceBubble") as Control).visible) + assert( + experience_presentation.get_index() + == experience_presentation.get_parent().get_child_count() - 1 + ) + assert(not (game_ui.get_node("%GameplayTransientHUD") as Control).visible) + assert(player_menu.visible) + assert(not jobs.claim(first_claim_id)) + assert( + player.wallet.get_balance() + == wallet_before + int(pending[0].get("fish_coin", 0)) + ) + assert( + player.experience.get_total_experience() + == experience_before + int(pending[0].get("experience", 0)) + ) + refreshed_sell = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.SELL_TOTAL + ) + assert(int(refreshed_sell.get("progress", -1)) == sell_target) + assert(int(refreshed_sell.get("completed_count", 0)) == 1) + assert(not bool(refreshed_sell.get("claimable", true))) + network_sale.local_sale_finished.emit( + "job-test-repeat", true, "sold", sold_ids, 1 + ) + await process_frame + assert(jobs.get_pending_rewards().is_empty()) + refreshed_sell = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.SELL_TOTAL + ) + assert(int(refreshed_sell.get("progress", -1)) == sell_target) + assert(int(refreshed_sell.get("completed_count", 0)) == 1) + + var fresh_job: Dictionary = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.CATCH_WATER + ) + assert(not fresh_job.is_empty()) + var bluegill: FishData = Catalog.get_fish_by_id(&"bluegill") + assert(bluegill != null) + for index: int in int(fresh_job.get("target", 0)): + var fish_catch := FishCatch.new() + fish_catch.fish = bluegill + fish_catch.fish_id = bluegill.id + fish_catch.catch_id = StringName("job-catch-%d" % index) + fish_catch.catch_sequence = index + 1 + fish_catch.weight_lb = bluegill.get_minimum_weight() + fish_catch.display_scale = bluegill.get_display_scale_for_weight( + fish_catch.weight_lb + ) + fish_catch.quality = FishQuality.Tier.BORING + fish_catch.sale_value = bluegill.get_sale_value_for_weight( + fish_catch.weight_lb + ) + assert(fish_catch.is_valid()) + network_fishing.local_catch_received.emit(fish_catch) + await process_frame + var refreshed_fresh: Dictionary = _find_job( + jobs.get_daily_jobs(), JobCatalog.Kind.CATCH_WATER + ) + assert(int(refreshed_fresh.get("completed_count", 0)) == 1) + + var pause_menu := game_ui.get_pause_menu() + var join_page := pause_menu.get_node("%JoinGamePage") as JoinGamePage + assert(join_page != null) + pause_menu.show() + join_page.set_status("Connection timed out.") + join_page.show() + join_page.call( + "_on_discovery_status_changed", "2 public rooms found", false + ) + var join_status := join_page.get_node("%Status") as Label + assert(join_status.text == "Could not reach the server.") + join_page.hide() + join_page.call("_set_mode", JoinGamePage.Mode.DIRECT) + join_page.call("_set_mode", JoinGamePage.Mode.DISCOVER) + join_page.show() + join_page.call( + "_on_discovery_status_changed", "2 public rooms found", false + ) + assert(join_status.text == "2 public rooms found") + join_page.close_page() + pause_menu.hide() + join_page.call("_refresh") + var session_summary := ( + join_page.get_node("%SessionSummary") as Label + ) + assert(not join_page.has_node( + "Paper/Margin/Layout/Actions/OpenCloseButton" + )) + assert("UDP 18138" in session_summary.text) + assert(session.set_host_open(true)) + await process_frame + assert("UDP 18138" in session_summary.text) + assert(session.set_host_open(false)) + + assert(save_manager.save_now()) + var save_file := FileAccess.open( + str(save_manager.get("_save_path")), FileAccess.READ + ) + assert(save_file != null) + var parsed: Variant = JSON.parse_string(save_file.get_as_text()) + save_file.close() + assert(typeof(parsed) == TYPE_DICTIONARY) + var save_data: Dictionary = parsed + assert(int(save_data.get("save_version", -1)) == 7) + assert(PlayerJobService.validate_save_data(save_data.get("jobs", {}))) + + _validate_pause_session_switch(main, session) + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + print("Job system validation: PASS") + quit() + + +func _validate_pause_session_switch( + main: Node, + session: NetworkSession, +) -> void: + var discovery := main.get_node("%DiscoveryClient") as DiscoveryClient + discovery.set("_pending_join_endpoint", "127.0.0.1:18141") + discovery.set("_pending_join_room_id", "session-switch-room") + discovery.set("_pending_join_token", "session-switch-token") + discovery.call( + "_set_public_join_state", + DiscoveryClient.PublicJoinState.CONNECTING, + ) + main.call("_on_pause_join_game_requested", "127.0.0.1:18141") + assert(session.state == NetworkSession.State.CONNECTING) + assert(discovery.get("_pending_join_token") == "session-switch-token") + var join_probe_timer := discovery.get("_join_probe_timer") as Timer + assert(join_probe_timer != null and not join_probe_timer.is_stopped()) + session.cancel_connection() + assert(session.state == NetworkSession.State.INACTIVE) + assert(str(discovery.get("_pending_join_token")).is_empty()) + + +func _validate_unavailable_fishnet_layout() -> void: + var unavailable_page := TheNetPage.new() + unavailable_page.size = Vector2(1280.0, 720.0) + root.add_child(unavailable_page) + for _frame: int in 3: + await process_frame + unavailable_page.call("_refresh_forecast", true) + await process_frame + var forecast_list := unavailable_page.get("_forecast_list") as HBoxContainer + assert(forecast_list != null) + assert(forecast_list.size.x >= 272.0) + assert(forecast_list.size.y <= 66.0) + assert(forecast_list.get_child_count() == 1) + var placeholder := forecast_list.get_child(0) as Label + assert(placeholder != null) + assert(placeholder.size.x >= 252.0) + assert(placeholder.size.y <= 66.0) + assert(placeholder.autowrap_mode == TextServer.AUTOWRAP_OFF) + var tabs := unavailable_page.get("_tabs") as HBoxContainer + assert(tabs != null) + assert(tabs.position.y + tabs.size.y < UtilityPageStyle.LAPTOP_RECT.end.y) + unavailable_page.queue_free() + for _frame: int in 2: + await process_frame + + +func _validate_weather_guarantees(board: Dictionary) -> void: + var jobs: Array = board.get("jobs", []) + var schedule: Array = board.get("weather_schedule", []) + var anchor_index: int = int(board.get("schedule_anchor_index", 0)) + for required_weather: int in JobCatalog.weather_requirements(jobs): + var occurrences: int = 0 + var has_later_window: bool = false + for index: int in schedule.size(): + var entry: Dictionary = schedule[index] + if int(entry.get("weather", -1)) != required_weather: + continue + occurrences += 1 + if index > anchor_index: + has_later_window = true + assert(occurrences >= 2) + assert(has_later_window) + + +func _validate_late_board_weather_fallback() -> void: + var late_jobs: Array[Dictionary] = JobCatalog.generate_daily_jobs( + "late-board-validation", [], false + ) + assert(JobCatalog.weather_requirements(late_jobs).is_empty()) + var anchored_jobs: Array[Dictionary] = JobCatalog.generate_daily_jobs( + "anchored-board-validation", [], true + ) + var anchored_schedule: Array[Dictionary] = ( + JobCatalog.generate_weather_schedule( + "anchored-board-validation", anchored_jobs, 9 + ) + ) + for required_weather: int in JobCatalog.weather_requirements(anchored_jobs): + var occurrence_indices: Array[int] = [] + for index: int in anchored_schedule.size(): + var entry: Dictionary = anchored_schedule[index] + if ( + index >= 9 + and int(entry.get("weather", -1)) == required_weather + ): + occurrence_indices.append(index) + assert(occurrence_indices.size() >= 2) + assert(occurrence_indices[0] >= 9) + assert(occurrence_indices[-1] > 9) + + +func _validate_remote_tamper_rejection( + jobs: PlayerJobService, + board: Dictionary, +) -> void: + var tampered: Dictionary = board.duplicate(true) + var tampered_jobs: Array = tampered.get("jobs", []) + var first_job: Dictionary = tampered_jobs[0] + first_job["fish_coin"] = int(first_job.get("fish_coin", 0)) + 1 + tampered_jobs[0] = first_job + tampered["jobs"] = tampered_jobs + assert(PlayerJobService.validate_board(tampered)) + assert(not jobs.apply_remote_board(tampered)) + + +func _find_job(jobs: Array[Dictionary], kind: JobCatalog.Kind) -> Dictionary: + for job: Dictionary in jobs: + if int(job.get("kind", -1)) == int(kind): + return job + return {} diff --git a/tests/job_system_validation.gd.uid b/tests/job_system_validation.gd.uid new file mode 100644 index 0000000..d37784b --- /dev/null +++ b/tests/job_system_validation.gd.uid @@ -0,0 +1 @@ +uid://b2grposd30lkw diff --git a/tests/logbook_runtime_validation.gd b/tests/logbook_runtime_validation.gd index b201b9a..88227aa 100644 --- a/tests/logbook_runtime_validation.gd +++ b/tests/logbook_runtime_validation.gd @@ -49,20 +49,20 @@ func _run() -> void: assert(not hotbar.visible) var entry_buttons: Dictionary = logbook.get("_entry_buttons") - assert(entry_buttons.size() == 6) + assert(entry_buttons.size() == 19) await _capture_if_requested("-unknown") logbook.call( "_select_category", WaterType.Type.FRESH_WATER ) await create_timer(0.25).timeout - assert((logbook.get("_entry_buttons") as Dictionary).size() == 6) + assert((logbook.get("_entry_buttons") as Dictionary).size() == 19) await _capture_if_requested("-fresh") logbook.call("_select_category", WaterType.Type.SALT_WATER) await create_timer(0.25).timeout - assert((logbook.get("_entry_buttons") as Dictionary).size() == 2) + assert((logbook.get("_entry_buttons") as Dictionary).size() == 34) logbook.call("_select_category", WaterType.Type.FRESH_WATER) await create_timer(0.25).timeout - assert((logbook.get("_entry_buttons") as Dictionary).size() == 6) + assert((logbook.get("_entry_buttons") as Dictionary).size() == 19) player.collection_log.mark_discovered(&"bluegill") await process_frame logbook.call("_select_entry", &"bluegill", &"bluegill") @@ -79,10 +79,15 @@ func _run() -> void: "_show_section_immediate", PlayerMenu.Section.PROFILE ) await process_frame - assert(not bool( + assert(bool( player_menu.call("_handle_direct_page_shortcut", shortcut) )) + await create_timer(0.5).timeout assert(player_menu.visible) + assert(logbook.visible) + assert( + player_menu.get("_current_section") == PlayerMenu.Section.LOGBOOK + ) player_menu.close_menu(PlayerMenu.CloseReason.TEARDOWN, false) print( @@ -90,7 +95,9 @@ func _run() -> void: % [root.size.x, root.size.y] ) main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -101,7 +108,7 @@ func _validate_save_round_trip( var player := main.get("_player") as Player var catalog := main.get("fish_catalog") as FishPool assert(catalog != null) - assert(catalog.candidates.size() == 8) + assert(catalog.candidates.size() == 53) for index: int in 4: _add_test_catch(player, catalog.candidates[index]) assert(save_manager.save_now()) @@ -122,7 +129,7 @@ func _validate_save_round_trip( assert(player.inventory.replace_all_catches(no_catches, 1)) assert(player.collection_log.replace_discovered_ids(no_discoveries)) assert(save_manager.load_player_data()) - assert(player.inventory.get_all_catches().size() == 8) + assert(player.inventory.get_all_catches().size() == 53) for fish: FishData in catalog.candidates: assert(player.inventory.get_count(fish.id) == 1) assert(player.collection_log.has_discovered(fish.id)) @@ -136,9 +143,10 @@ func _validate_save_round_trip( player.inventory.get_catches_by_fish_id(fish_id).front() ) player.begin_catch_showcase(fish_catch) - var catch_sprite := player.get_node( - "%CatchSprite" + var catch_sprite := player.find_child( + "CatchSprite", true, false ) as Sprite3D + assert(catch_sprite != null) assert(catch_sprite.texture == fish_catch.fish.display_texture) player.end_catch_showcase(Callable(), true) diff --git a/tests/logbook_validation.gd b/tests/logbook_validation.gd index 4c25e35..866a34f 100644 --- a/tests/logbook_validation.gd +++ b/tests/logbook_validation.gd @@ -49,20 +49,103 @@ func _validate_catalog() -> void: &"catfish_channel", &"catfish_flathead", &"catfish_white", + &"tuna_albacore", + &"tuna_bigeye", + &"tuna_bluefin", + &"tuna_skipjack", + &"tuna_yellowfin", + &"goby_round", + &"salmon_atlantic", + &"salmon_chum", + &"salmon_coho", + &"salmon_pink", + &"salmon_sockeye", + &"anchovy_european", + &"anchovy_northern", + &"chub_european", + &"chub_flame", + &"chub_lake", + &"goldfish", + &"goldfish_bubbleeye", + &"grouper_gulf", + &"grouper_red", + &"mackerel_atlantic", + &"mackerel_cero", + &"mackerel_chub", + &"mackerel_king", + &"mackerel_spanish", + &"marlin_black", + &"marlin_blue", + &"marlin_white", + &"pomfret_black", + &"pomfret_chinese", + &"pomfret_golden", + &"pomfret_white", + &"sailfish", + &"sauger", + &"saugeye", + &"snapper_lane", + &"snapper_mangrove", + &"snapper_mutton", + &"snapper_red", + &"swordfish", + &"trout_cutthroat", + &"trout_golden", + &"trout_rainbow", + &"trout_steelhead", + &"walleye", ] assert(LogbookCatalog.CATALOG_ORDER == expected_ids) for index: int in expected_ids.size(): var fish := CatalogResource.get_fish_by_id(expected_ids[index]) assert(fish != null) - assert(LogbookCatalog.facts_for(fish.id) != "unknown") assert(not LogbookCatalog.facts_for(fish.id).is_empty()) assert( LogbookCatalog.facts_for(fish.id) == LogbookCatalog.facts_for(fish.id).to_lower() ) + if LogbookCatalog.FISH_FACTS.has(fish.id): + assert(LogbookCatalog.facts_for(fish.id) != "unknown") + else: + assert(LogbookCatalog.facts_for(fish.id) == "unknown") var expected_category: WaterType.Type = ( WaterType.Type.SALT_WATER - if expected_ids[index] in [&"bass", &"sunfish"] + if expected_ids[index] in [ + &"bass", + &"sunfish", + &"tuna_albacore", + &"tuna_bigeye", + &"tuna_bluefin", + &"tuna_skipjack", + &"tuna_yellowfin", + &"salmon_atlantic", + &"salmon_chum", + &"salmon_coho", + &"salmon_pink", + &"salmon_sockeye", + &"anchovy_european", + &"anchovy_northern", + &"grouper_gulf", + &"grouper_red", + &"mackerel_atlantic", + &"mackerel_cero", + &"mackerel_chub", + &"mackerel_king", + &"mackerel_spanish", + &"marlin_black", + &"marlin_blue", + &"marlin_white", + &"pomfret_black", + &"pomfret_chinese", + &"pomfret_golden", + &"pomfret_white", + &"sailfish", + &"snapper_lane", + &"snapper_mangrove", + &"snapper_mutton", + &"snapper_red", + &"swordfish", + ] else WaterType.Type.FRESH_WATER ) assert( @@ -88,7 +171,7 @@ func _validate_page() -> void: page.activate() await process_frame assert(page.get("_category") == WaterType.Type.FRESH_WATER) - assert((page.get("_catalog_grid") as GridContainer).columns == 5) + assert((page.get("_catalog_grid") as GridContainer).columns == 4) var initial_detail_body := page.get("_detail_body") as VBoxContainer assert(not initial_detail_body.get_parent() is ScrollContainer) assert( @@ -105,7 +188,7 @@ func _validate_page() -> void: page.call("_select_category", category) await create_timer(0.25).timeout var entries: Dictionary = page.get("_entry_buttons") - assert(entries.size() == (6 if category == WaterType.Type.FRESH_WATER else 2)) + assert(entries.size() == (19 if category == WaterType.Type.FRESH_WATER else 34)) for fish: FishDataType in LogbookCatalog.ordered_species( CatalogResource.candidates ): @@ -126,7 +209,7 @@ func _validate_page() -> void: assert(portrait.source_texture == fish.display_texture) assert( portrait.custom_minimum_size - == LogbookPortraitType.ENTRY_FRAME_SIZE + == LogbookPage.CATALOG_PORTRAIT_SIZE ) assert( portrait.expand_mode @@ -155,7 +238,7 @@ func _validate_page() -> void: candidate.display_name ) ) - assert(silhouette_count == 8) + assert(silhouette_count == 53) page.call("_select_category", WaterType.Type.OTHER) await create_timer(0.25).timeout diff --git a/tests/movement_multiplayer_validation.gd b/tests/movement_multiplayer_validation.gd new file mode 100644 index 0000000..cb1d4eb --- /dev/null +++ b/tests/movement_multiplayer_validation.gd @@ -0,0 +1,163 @@ +extends SceneTree + +const MainScene: PackedScene = preload("res://main/main.tscn") +const TEST_PORT: int = 18141 + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var arguments: PackedStringArray = OS.get_cmdline_user_args() + if arguments.has("host"): + await _run_host() + return + if arguments.has("client"): + await _run_client() + return + push_error("Movement multiplayer validation needs host or client mode.") + quit(1) + + +func _run_host() -> void: + var main: Node = await _create_initialized_main() + var session := main.get_node("%NetworkSession") as NetworkSession + var save_manager := main.get("_save_manager") as PlayerSaveManager + assert(session.start_private_host(TEST_PORT)) + assert(save_manager.initialize_new_game()) + main.call("_enter_gameplay") + for _frame: int in 4: + await physics_frame + assert(session.set_host_open(true)) + var remote_peer_id: int = await _wait_for_remote_peer(session) + assert(remote_peer_id > 1) + var player := main.get("_player") as Player + player.configure_network_remote(true) + player.apply_authoritative_network_input(_movement_input(1, true)) + await create_timer(2.5).timeout + player.apply_authoritative_network_input(_movement_input(2, false)) + await create_timer(2.5).timeout + player.apply_authoritative_network_input(_movement_input(3, false, false)) + var disconnect_deadline: int = Time.get_ticks_msec() + 8000 + while ( + Time.get_ticks_msec() < disconnect_deadline + and session.is_authenticated_peer(remote_peer_id) + ): + await process_frame + assert(not session.is_authenticated_peer(remote_peer_id)) + print("Movement multiplayer host validation: PASS") + session.disconnect_session("") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _run_client() -> void: + var main: Node = await _create_initialized_main() + main.call( + "_on_title_join_game_requested", + "127.0.0.1:%d" % TEST_PORT, + ) + var session := main.get_node("%NetworkSession") as NetworkSession + var join_deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < join_deadline: + await process_frame + if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY: + main.call("_confirm_server_trust") + if session.is_joined_client() and bool(main.get("_gameplay_started")): + break + assert(session.is_joined_client()) + var spawn_service := main.get_node( + "%PlayerSpawnService" + ) as PlayerSpawnService + var host_avatar: Player = spawn_service.get_avatar(1) + assert(host_avatar != null) + var animation_player := host_avatar.get_node( + "Visuals/CharacterRig/AnimationPlayer" + ) as AnimationPlayer + var sprint_dust := host_avatar.get_node("%SprintDust") as SprintDustTrail + var saw_running: bool = false + var saw_walking: bool = false + var saw_animation_advance: bool = false + var saw_dust: bool = false + var previous_animation: StringName = &"" + var previous_animation_position: float = -1.0 + var observation_deadline: int = Time.get_ticks_msec() + 7000 + while Time.get_ticks_msec() < observation_deadline: + await process_frame + var current_animation: StringName = animation_player.current_animation + saw_running = saw_running or current_animation.begins_with("running") + saw_walking = saw_walking or current_animation.begins_with("walking") + var current_position: float = ( + animation_player.current_animation_position + ) + if ( + current_animation == previous_animation + and previous_animation_position >= 0.0 + and absf(current_position - previous_animation_position) > 0.001 + ): + saw_animation_advance = true + previous_animation = current_animation + previous_animation_position = current_position + saw_dust = saw_dust or sprint_dust.get_active_puff_count() > 0 + if saw_running and saw_walking and saw_animation_advance and saw_dust: + break + assert(saw_running) + assert(saw_walking) + assert(saw_animation_advance) + assert(saw_dust) + print("Movement multiplayer client validation: PASS") + session.disconnect_session("") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _movement_input( + sequence: int, + sprinting: bool, + moving: bool = true, +) -> Dictionary: + return { + "sequence": sequence, + "axis": [0.0, -1.0] if moving else [0.0, 0.0], + "camera_yaw": 0.0, + "jump": false, + "sprint": sprinting, + "sneak": false, + "slow_walk": false, + "sitting": false, + "casting": false, + "animation_action": ( + NetworkPlayerAnimationProtocol.make_action_state() + ), + } + + +func _create_initialized_main() -> Node: + root.size = Vector2i(1280, 720) + var main: Node = MainScene.instantiate() + root.add_child(main) + for _frame: int in 4: + await process_frame + if not bool(main.get("_application_initialized")): + main.call("_activate_selected_data_path", "", true) + for _frame: int in 8: + await process_frame + assert(bool(main.get("_application_initialized"))) + return main + + +func _wait_for_remote_peer(session: NetworkSession) -> int: + var deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < deadline: + await process_frame + for peer_id: int in session.get_authenticated_peer_ids(): + if peer_id != session.get_local_peer_id(): + return peer_id + return 0 diff --git a/tests/movement_multiplayer_validation.gd.uid b/tests/movement_multiplayer_validation.gd.uid new file mode 100644 index 0000000..4d8c726 --- /dev/null +++ b/tests/movement_multiplayer_validation.gd.uid @@ -0,0 +1 @@ +uid://dlpu5lwb2pges diff --git a/tests/network_player_animation_protocol_validation.gd b/tests/network_player_animation_protocol_validation.gd new file mode 100644 index 0000000..8c3474c --- /dev/null +++ b/tests/network_player_animation_protocol_validation.gd @@ -0,0 +1,49 @@ +extends SceneTree + + +func _initialize() -> void: + var idle := NetworkPlayerAnimationProtocol.make_state( + NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE, + true, + ) + assert(NetworkPlayerAnimationProtocol.validate_state(idle)) + var emote := NetworkPlayerAnimationProtocol.make_state( + NetworkPlayerAnimationProtocol.LOCOMOTION_RUNNING, + false, + &"wave_hello", + 17, + 1.25, + ) + assert(NetworkPlayerAnimationProtocol.validate_state(emote)) + var future_locomotion := NetworkPlayerAnimationProtocol.make_state( + &"swimming_fast", + false, + ) + assert(NetworkPlayerAnimationProtocol.validate_state(future_locomotion)) + var extra_future_field: Dictionary = emote.duplicate(true) + extra_future_field["future_layer"] = {"id": "umbrella"} + assert(NetworkPlayerAnimationProtocol.validate_state(extra_future_field)) + + var invalid_action: Dictionary = emote.duplicate(true) + invalid_action["action"] = { + "id": "../unsafe", + "sequence": 17, + "elapsed": 1.25, + } + assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_action)) + var invalid_sequence: Dictionary = emote.duplicate(true) + invalid_sequence["action"] = { + "id": "wave_hello", + "sequence": -1, + "elapsed": 1.25, + } + assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_sequence)) + var invalid_elapsed: Dictionary = emote.duplicate(true) + invalid_elapsed["action"] = { + "id": "wave_hello", + "sequence": 17, + "elapsed": INF, + } + assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_elapsed)) + print("Network player animation protocol validation: PASS") + quit() diff --git a/tests/network_player_animation_protocol_validation.gd.uid b/tests/network_player_animation_protocol_validation.gd.uid new file mode 100644 index 0000000..017146d --- /dev/null +++ b/tests/network_player_animation_protocol_validation.gd.uid @@ -0,0 +1 @@ +uid://cjgelp62dkykn diff --git a/tests/on_screen_keyboard_validation.gd b/tests/on_screen_keyboard_validation.gd new file mode 100644 index 0000000..e794cda --- /dev/null +++ b/tests/on_screen_keyboard_validation.gd @@ -0,0 +1,82 @@ +extends SceneTree + +const KeyboardType = preload("res://ui/on_screen_keyboard.gd") +const SettingsManagerType = preload( + "res://settings/player_settings_manager.gd" +) + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + _validate_default_and_persistence() + await _validate_keyboard_entry() + print("On-screen keyboard validation: PASS") + quit() + + +func _validate_default_and_persistence() -> void: + var defaults := PlayerSettings.new() + assert(not defaults.on_screen_keyboard_enabled) + var manager := SettingsManagerType.new() + root.add_child(manager) + assert(manager.load_settings()) + var edited: PlayerSettings = manager.current_settings.copy() + edited.on_screen_keyboard_enabled = true + assert(manager.apply_settings(edited)) + var reloaded := SettingsManagerType.new() + root.add_child(reloaded) + assert(reloaded.load_settings()) + assert(reloaded.current_settings.on_screen_keyboard_enabled) + manager.queue_free() + reloaded.queue_free() + + +func _validate_keyboard_entry() -> void: + var host := Control.new() + root.add_child(host) + var edit := LineEdit.new() + edit.focus_mode = Control.FOCUS_ALL + host.add_child(edit) + var keyboard := KeyboardType.new() + host.add_child(keyboard) + await process_frame + keyboard.set_enabled(true) + edit.grab_focus() + var activate_event := InputEventJoypadButton.new() + activate_event.button_index = JOY_BUTTON_A + activate_event.pressed = true + keyboard.call("_input", activate_event) + assert(keyboard.is_open()) + keyboard.call("_type_character", "a") + keyboard.call("_type_space") + keyboard.call("_set_page", KeyboardType.Page.UPPER) + keyboard.call("_type_character", "B") + assert(edit.text == "a B") + keyboard.call("_move_caret", -1) + keyboard.call("_type_character", "C") + assert(edit.text == "a CB") + var backspace_event := InputEventJoypadButton.new() + backspace_event.button_index = JOY_BUTTON_X + backspace_event.pressed = true + keyboard.call("_input", backspace_event) + assert(edit.text == "a B") + var defocus_event := InputEventJoypadButton.new() + defocus_event.button_index = JOY_BUTTON_LEFT_SHOULDER + defocus_event.pressed = true + keyboard.call("_input", defocus_event) + assert(not keyboard.is_open()) + assert(root.gui_get_focus_owner() == null) + edit.grab_focus() + keyboard.call("_input", activate_event) + assert(keyboard.is_open()) + var submitted: Array[String] = [] + edit.text_submitted.connect(func(value: String) -> void: + submitted.append(value) + ) + keyboard.call("_submit") + assert(not keyboard.is_open()) + assert(submitted == ["a B"]) + host.queue_free() diff --git a/tests/on_screen_keyboard_validation.gd.uid b/tests/on_screen_keyboard_validation.gd.uid new file mode 100644 index 0000000..973bdca --- /dev/null +++ b/tests/on_screen_keyboard_validation.gd.uid @@ -0,0 +1 @@ +uid://bgmt6joqdk37q diff --git a/tests/operator_multiplayer_validation.gd b/tests/operator_multiplayer_validation.gd new file mode 100644 index 0000000..3712fd4 --- /dev/null +++ b/tests/operator_multiplayer_validation.gd @@ -0,0 +1,253 @@ +extends SceneTree + +const MainScene = preload("res://main/main.tscn") +const TEST_PORT: int = 18140 +const FIRST_BANNED_FINGERPRINT: String = ( + "1111111111111111111111111111111111111111111111111111111111111111" +) +const SECOND_BANNED_FINGERPRINT: String = ( + "2222222222222222222222222222222222222222222222222222222222222222" +) + +var _moderation_results: Array[Dictionary] = [] + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var arguments: PackedStringArray = OS.get_cmdline_user_args() + if arguments.has("host"): + await _run_host() + return + if arguments.has("client"): + await _run_client() + return + push_error("Operator multiplayer validation needs host or client mode.") + quit(1) + + +func _run_host() -> void: + var main: Node = await _create_initialized_main() + var session := main.get_node("%NetworkSession") as NetworkSession + var service := ( + main.get_node("%NetworkPlayerListService") + as NetworkPlayerListService + ) + var bans := main.get_node("%HostBanStore") as HostBanStore + assert(session.start_private_host(TEST_PORT)) + assert(session.set_host_open(true)) + + var remote_peer_id: int = await _wait_for_remote_peer(session) + assert(remote_peer_id > 1) + var remote_record: PeerRegistry.PeerRecord = session.get_peer_record( + remote_peer_id + ) + assert(remote_record != null and remote_record.identity_authenticated) + var host_fingerprint: String = session.get_host_identity_fingerprint() + assert(bans.ban( + host_fingerprint, FIRST_BANNED_FINGERPRINT, "First Banned Player" + )) + + var entry: PlayerListEntry = _entry_for_peer(service, remote_peer_id) + assert(entry != null and entry.can_manage_operator) + assert(service.set_operator( + remote_peer_id, + remote_record.identity_fingerprint, + true, + entry.revision, + )) + assert(session.is_peer_operator(remote_peer_id)) + var players_page := main.find_child( + "PlayersPage", true, false + ) as PlayersPage + assert(players_page != null) + players_page.call("_refresh") + assert(_has_button_text(players_page, "deop")) + + var unban_deadline: int = Time.get_ticks_msec() + 12000 + while ( + Time.get_ticks_msec() < unban_deadline + and bans.is_banned(host_fingerprint, FIRST_BANNED_FINGERPRINT) + ): + await process_frame + assert(not bans.is_banned( + host_fingerprint, FIRST_BANNED_FINGERPRINT + )) + assert(bans.ban( + host_fingerprint, SECOND_BANNED_FINGERPRINT, "Second Banned Player" + )) + + entry = _entry_for_peer(service, remote_peer_id) + assert(entry != null and entry.is_operator) + assert(service.set_operator( + remote_peer_id, + remote_record.identity_fingerprint, + false, + entry.revision, + )) + assert(not session.is_peer_operator(remote_peer_id)) + await create_timer(2.0).timeout + assert(session.kick_authenticated_peer( + remote_peer_id, + remote_record.identity_fingerprint, + )) + + var disconnect_deadline: int = Time.get_ticks_msec() + 8000 + while ( + Time.get_ticks_msec() < disconnect_deadline + and session.is_authenticated_peer(remote_peer_id) + ): + await process_frame + assert(not session.is_authenticated_peer(remote_peer_id)) + assert(bans.is_banned(host_fingerprint, SECOND_BANNED_FINGERPRINT)) + print("Operator multiplayer host validation: PASS") + session.disconnect_session("") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _run_client() -> void: + var main: Node = await _create_initialized_main() + main.call( + "_on_title_join_game_requested", "127.0.0.1:%d" % TEST_PORT + ) + var session := main.get_node("%NetworkSession") as NetworkSession + var service := ( + main.get_node("%NetworkPlayerListService") + as NetworkPlayerListService + ) + service.moderation_finished.connect( + func(success: bool, message: String) -> void: + _moderation_results.append({ + "success": success, + "message": message, + }) + ) + var join_deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < join_deadline: + await process_frame + if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY: + main.call("_confirm_server_trust") + if session.is_joined_client(): + break + assert(session.is_joined_client()) + + var operator_deadline: int = Time.get_ticks_msec() + 10000 + while Time.get_ticks_msec() < operator_deadline and not session.is_local_operator(): + await process_frame + assert(session.is_local_operator()) + assert(service.is_local_moderator()) + var players_page := main.find_child( + "PlayersPage", true, false + ) as PlayersPage + assert(players_page != null) + players_page.call("_refresh") + var tabs := players_page.get("_tabs") as HBoxContainer + assert((tabs.get_child(2) as Button).visible) + players_page.call("_select_tab", 2) + assert(int(players_page.get("_current_tab")) == 2) + var local_entry: PlayerListEntry = _entry_for_peer( + service, session.get_local_peer_id() + ) + assert(local_entry != null and local_entry.is_operator) + + var ban_deadline: int = Time.get_ticks_msec() + 10000 + while ( + Time.get_ticks_msec() < ban_deadline + and not _has_ban(service.get_bans(), FIRST_BANNED_FINGERPRINT) + ): + await process_frame + assert(_has_ban(service.get_bans(), FIRST_BANNED_FINGERPRINT)) + assert(service.unban(FIRST_BANNED_FINGERPRINT)) + var result_deadline: int = Time.get_ticks_msec() + 8000 + while Time.get_ticks_msec() < result_deadline and _moderation_results.is_empty(): + await process_frame + assert(not _moderation_results.is_empty()) + assert(bool(_moderation_results.back().get("success", false))) + + var deop_deadline: int = Time.get_ticks_msec() + 10000 + while Time.get_ticks_msec() < deop_deadline and session.is_local_operator(): + await process_frame + assert(not session.is_local_operator()) + assert(not service.is_local_moderator()) + assert(not (tabs.get_child(2) as Button).visible) + assert(int(players_page.get("_current_tab")) == 0) + service.request_unban.rpc_id(1, SECOND_BANNED_FINGERPRINT) + var disconnect_deadline: int = Time.get_ticks_msec() + 10000 + while ( + Time.get_ticks_msec() < disconnect_deadline + and session.state != NetworkSession.State.SERVER_LOST + ): + await process_frame + assert(session.state == NetworkSession.State.SERVER_LOST) + assert(not bool(main.get("_gameplay_started"))) + var game_ui := main.get_node("%GameUI") as GameUI + var title_screen := game_ui.get_title_screen() + assert(title_screen.visible) + assert(not title_screen.is_awaiting_start_input()) + assert((title_screen.get_node("%Center") as Control).visible) + assert((title_screen.get_node("%ButtonCenter") as Control).visible) + assert(not (title_screen.get_node("%JoinGamePage") as Control).visible) + var feedback := title_screen.get_node("%FeedbackLabel") as RichTextLabel + assert(feedback.visible) + assert("removed by the host" in feedback.text.to_lower()) + print("Operator multiplayer client validation: PASS") + main.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout + quit() + + +func _create_initialized_main() -> Node: + root.size = Vector2i(1280, 720) + var main: Node = MainScene.instantiate() + root.add_child(main) + for _frame: int in 4: + await process_frame + if not bool(main.get("_application_initialized")): + main.call("_activate_selected_data_path", "", true) + for _frame: int in 8: + await process_frame + assert(bool(main.get("_application_initialized"))) + return main + + +func _wait_for_remote_peer(session: NetworkSession) -> int: + var deadline: int = Time.get_ticks_msec() + 20000 + while Time.get_ticks_msec() < deadline: + await process_frame + for peer_id: int in session.get_authenticated_peer_ids(): + if peer_id != session.get_local_peer_id(): + return peer_id + return 0 + + +func _entry_for_peer( + service: NetworkPlayerListService, + peer_id: int, +) -> PlayerListEntry: + for entry: PlayerListEntry in service.get_entries(): + if entry.peer_id == peer_id: + return entry + return null + + +func _has_ban(records: Array[Dictionary], fingerprint: String) -> bool: + for record: Dictionary in records: + if str(record.get("target_fingerprint", "")) == fingerprint: + return true + return false + + +func _has_button_text(root_node: Node, text: String) -> bool: + for node: Node in root_node.find_children("*", "Button", true, false): + var button := node as Button + if button != null and button.text == text: + return true + return false diff --git a/tests/operator_multiplayer_validation.gd.uid b/tests/operator_multiplayer_validation.gd.uid new file mode 100644 index 0000000..2951ea6 --- /dev/null +++ b/tests/operator_multiplayer_validation.gd.uid @@ -0,0 +1 @@ +uid://cv17xsf20ue5t diff --git a/tests/player_experience_ui_validation.gd b/tests/player_experience_ui_validation.gd index 93c23eb..31ed497 100644 --- a/tests/player_experience_ui_validation.gd +++ b/tests/player_experience_ui_validation.gd @@ -12,6 +12,14 @@ func _run() -> void: root.add_child(game_ui) await process_frame game_ui.set("_gameplay_ui_enabled", true) + var panel := game_ui.get_node("%ExperienceProgressPanel") as PanelContainer + var bubble := game_ui.get_node("%ExperienceBubble") as PanelContainer + var bubble_label := game_ui.get_node("%ExperienceBubbleLabel") as Label + var award_label := game_ui.get_node("%ExperienceAwardLabel") as Label + assert(panel != null and bubble != null) + assert(not panel.visible and not bubble.visible) + game_ui.call("_on_showcase_changed", "bluegill", "common", 1.0, 0, true) + await process_frame game_ui.call( "_on_experience_awarded", 50, @@ -20,13 +28,6 @@ func _run() -> void: 1, 1, ) - var panel := game_ui.get_node("%ExperienceProgressPanel") as PanelContainer - var bubble := game_ui.get_node("%ExperienceBubble") as PanelContainer - var bubble_label := game_ui.get_node("%ExperienceBubbleLabel") as Label - var award_label := game_ui.get_node("%ExperienceAwardLabel") as Label - assert(panel != null and bubble != null) - assert(not panel.visible and not bubble.visible) - game_ui.call("_on_showcase_changed", "bluegill", "common", 1.0, 0, true) await process_frame assert(not panel.visible and not bubble.visible) game_ui.call("_on_showcase_changed", "", "", 0.0, 0, false) @@ -43,5 +44,8 @@ func _run() -> void: await create_timer(1.2).timeout assert(not panel.visible and not bubble.visible) game_ui.queue_free() + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout print("Player experience UI validation: PASS") quit() diff --git a/tests/player_experience_validation.gd b/tests/player_experience_validation.gd index e7b8c18..c9b36cd 100644 --- a/tests/player_experience_validation.gd +++ b/tests/player_experience_validation.gd @@ -143,11 +143,14 @@ func _validate_save_migration() -> void: version_five, 5, ) - assert(int(migrated.get("save_version", -1)) == 6) + assert(int(migrated.get("save_version", -1)) == 7) var experience_data: Dictionary = migrated.get("experience", {}) assert(int(experience_data.get("total_experience", -1)) == 0) var world_data: Dictionary = migrated.get("world", {}) assert(is_equal_approx(float(world_data.get("time_hours", -1.0)), 8.0)) + assert( + PlayerJobService.validate_save_data(migrated.get("jobs", {})) + ) manager.queue_free() diff --git a/tests/shoreline_ambience_validation.gd b/tests/shoreline_ambience_validation.gd new file mode 100644 index 0000000..4297d72 --- /dev/null +++ b/tests/shoreline_ambience_validation.gd @@ -0,0 +1,48 @@ +extends SceneTree + +const ShorelineAmbienceType := preload("res://world/shoreline_ambience.gd") +const WavesStream := preload("res://audio/ambience/waves.wav") + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var waves := WavesStream as AudioStreamWAV + assert(waves != null) + + var segments: Array[PackedVector2Array] = [ + PackedVector2Array([Vector2.ZERO, Vector2(10.0, 0.0)]), + ] + assert(is_equal_approx( + ShorelineAmbienceType.distance_to_segments(Vector2(5.0, 3.0), segments), + 3.0, + )) + assert(is_equal_approx( + ShorelineAmbienceType.distance_to_segments(Vector2(-4.0, 0.0), segments), + 4.0, + )) + + var controller := ShorelineAmbienceType.new() as ShorelineAmbience + var runtime_audio := AudioStreamPlayer.new() + runtime_audio.name = "WavesAudio" + runtime_audio.unique_name_in_owner = true + runtime_audio.stream = WavesStream + runtime_audio.bus = &"SFX" + controller.add_child(runtime_audio) + runtime_audio.owner = controller + root.add_child(controller) + await process_frame + assert(runtime_audio.bus == &"Environment") + assert(controller.near_distance < controller.far_distance) + assert(controller.near_volume_db > controller.far_volume_db) + var runtime_waves := runtime_audio.stream as AudioStreamWAV + assert(runtime_waves.loop_mode == AudioStreamWAV.LOOP_FORWARD) + assert(runtime_waves.loop_begin == 0) + assert(runtime_waves.loop_end == 1044956) + controller.free() + await process_frame + + print("Shoreline ambience validation: PASS") + quit() diff --git a/tests/shoreline_ambience_validation.gd.uid b/tests/shoreline_ambience_validation.gd.uid new file mode 100644 index 0000000..4b7987b --- /dev/null +++ b/tests/shoreline_ambience_validation.gd.uid @@ -0,0 +1 @@ +uid://bwvhgpbkb00l1 diff --git a/tests/shoreline_ribbon_validation.gd b/tests/shoreline_ribbon_validation.gd new file mode 100644 index 0000000..e78acb0 --- /dev/null +++ b/tests/shoreline_ribbon_validation.gd @@ -0,0 +1,97 @@ +extends SceneTree + +const ShorelineMesh: ArrayMesh = preload( + "res://world/generated/shorelines/starter_ocean_shoreline.tres" +) +const GEOMETRY_EPSILON := 0.0001 +const CROSSING_NEIGHBORHOOD := 32 + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + assert(ShorelineMesh.get_surface_count() == 1) + var arrays := ShorelineMesh.surface_get_arrays(0) + var vertices := arrays[Mesh.ARRAY_VERTEX] as PackedVector3Array + var indices := arrays[Mesh.ARRAY_INDEX] as PackedInt32Array + assert(not vertices.is_empty()) + assert(vertices.size() % 2 == 0) + assert(indices.size() == vertices.size() * 3) + var point_count := vertices.size() / 2 + var land_edge := PackedVector2Array() + var water_edge := PackedVector2Array() + for index: int in point_count: + var land := vertices[index * 2] + var water := vertices[index * 2 + 1] + land_edge.append(Vector2(land.x, land.z)) + water_edge.append(Vector2(water.x, water.z)) + var ribbon_width := land.distance_to(water) + assert(ribbon_width >= 0.34) + assert(ribbon_width <= 1.5) + var crossing_count := ( + _count_local_crossings(water_edge) + + _count_local_crossings(land_edge) + ) + if crossing_count > 0: + push_error( + "Shoreline ribbon contains %d local edge crossings." + % crossing_count + ) + quit(1) + return + print("Shoreline ribbon validation: PASS") + quit() + + +func _count_local_crossings(points: PackedVector2Array) -> int: + var crossing_count := 0 + for first: int in points.size(): + var first_next := (first + 1) % points.size() + var checked_neighbors := mini( + CROSSING_NEIGHBORHOOD, + points.size() - 2, + ) + for offset: int in range(2, checked_neighbors + 1): + var second := (first + offset) % points.size() + var second_next := (second + 1) % points.size() + if second_next == first: + continue + if _segments_cross( + points[first], + points[first_next], + points[second], + points[second_next], + ): + crossing_count += 1 + return crossing_count + + +func _segments_cross( + a: Vector2, + b: Vector2, + c: Vector2, + d: Vector2, +) -> bool: + if ( + maxf(a.x, b.x) < minf(c.x, d.x) - GEOMETRY_EPSILON + or maxf(c.x, d.x) < minf(a.x, b.x) - GEOMETRY_EPSILON + or maxf(a.y, b.y) < minf(c.y, d.y) - GEOMETRY_EPSILON + or maxf(c.y, d.y) < minf(a.y, b.y) - GEOMETRY_EPSILON + ): + return false + var first_direction := b - a + var second_direction := d - c + var denominator := first_direction.cross(second_direction) + if absf(denominator) <= GEOMETRY_EPSILON: + return false + var offset := c - a + var first_amount := offset.cross(second_direction) / denominator + var second_amount := offset.cross(first_direction) / denominator + return ( + first_amount > GEOMETRY_EPSILON + and first_amount < 1.0 - GEOMETRY_EPSILON + and second_amount > GEOMETRY_EPSILON + and second_amount < 1.0 - GEOMETRY_EPSILON + ) diff --git a/tests/shoreline_ribbon_validation.gd.uid b/tests/shoreline_ribbon_validation.gd.uid new file mode 100644 index 0000000..7989d89 --- /dev/null +++ b/tests/shoreline_ribbon_validation.gd.uid @@ -0,0 +1 @@ +uid://c73frygpfhred diff --git a/tests/surface_drawing_multiplayer_validation.gd b/tests/surface_drawing_multiplayer_validation.gd index 12925ba..74c86d2 100644 --- a/tests/surface_drawing_multiplayer_validation.gd +++ b/tests/surface_drawing_multiplayer_validation.gd @@ -32,6 +32,8 @@ func _run_host() -> void: await physics_frame var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( @@ -114,7 +116,9 @@ func _run_host() -> void: print("Surface drawing multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -138,6 +142,8 @@ func _run_client() -> void: )) var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( @@ -207,7 +213,9 @@ func _run_client() -> void: print("Surface drawing multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() diff --git a/tests/surface_drawing_runtime_validation.gd b/tests/surface_drawing_runtime_validation.gd index d730a6a..8835127 100644 --- a/tests/surface_drawing_runtime_validation.gd +++ b/tests/surface_drawing_runtime_validation.gd @@ -29,12 +29,15 @@ func _run() -> void: var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( "%NetworkSurfaceDrawingService" ) as NetworkSurfaceDrawingService assert(service != null) assert(session.supports_server_capability(SurfaceDrawingProtocol.CAPABILITY)) + service.deactivate() _validate_marker_controls(service, player) var query := PhysicsRayQueryParameters3D.create( player.global_position + Vector3.UP * 3.0, @@ -110,7 +113,9 @@ func _run() -> void: print("Surface drawing runtime validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -119,9 +124,11 @@ func _validate_marker_controls( player: Player, ) -> void: var prior_mouse_mode: Input.MouseMode = Input.mouse_mode - service.activate() + var activation_pointer := Vector2(824.0, 318.0) + service.activate(activation_pointer) assert(service.is_active()) - assert(service.is_placement_mode()) + assert(not service.is_placement_mode()) + assert(service.get_pointer_screen_position() == activation_pointer) if DisplayServer.get_name() != "headless": assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE) @@ -129,13 +136,22 @@ func _validate_marker_controls( placement_key.physical_keycode = KEY_R placement_key.pressed = true assert(service.handle_input(placement_key, true)) - assert(not service.is_placement_mode()) + assert(service.is_placement_mode()) var pointer_before: Vector2 = service.get_pointer_screen_position() var pointer_motion := InputEventMouseMotion.new() pointer_motion.position = pointer_before + Vector2(30.0, -12.0) assert(service.handle_input(pointer_motion, true)) assert(service.get_pointer_screen_position() != pointer_before) + var scaled_ui_motion := InputEventMouseMotion.new() + scaled_ui_motion.position = Vector2(260.0, 140.0) + var root_viewport_pointer := Vector2(960.0, 540.0) + assert(service.handle_input( + scaled_ui_motion, + true, + root_viewport_pointer, + )) + assert(service.get_pointer_screen_position() == root_viewport_pointer) var zoom_event := InputEventMouseButton.new() zoom_event.button_index = MOUSE_BUTTON_WHEEL_UP zoom_event.shift_pressed = true @@ -158,6 +174,6 @@ func _validate_marker_controls( assert(not service.handle_input(camera_release, true)) assert(service.handle_input(placement_key, true)) - assert(service.is_placement_mode()) + assert(not service.is_placement_mode()) service.deactivate() assert(Input.mouse_mode == prior_mouse_mode) diff --git a/tests/surface_drawing_validation.gd b/tests/surface_drawing_validation.gd index aa94f0f..596cec0 100644 --- a/tests/surface_drawing_validation.gd +++ b/tests/surface_drawing_validation.gd @@ -21,6 +21,8 @@ func _run() -> void: await _validate_blocked_author_visibility() _validate_peer_capability_tracking() print("Surface drawing validation: PASS") + for _frame: int in 4: + await process_frame quit() @@ -118,6 +120,7 @@ func _validate_art_unlocks() -> void: assert(unlocks.is_brush_size_unlocked(4)) assert(unlocks.is_grid_size_unlocked(128)) assert(not unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK + 1)) + unlocks.free() func _validate_grid_snapping() -> void: @@ -265,6 +268,7 @@ func _validate_blocked_author_visibility() -> void: }) var world := Node3D.new() root.add_child(world) + world.add_child(relationships) var canvas := SurfaceDrawingCanvas.new() world.add_child(canvas) var state: Dictionary = { diff --git a/tests/terrain_blender_material_validation.gd b/tests/terrain_blender_material_validation.gd new file mode 100644 index 0000000..fe3de22 --- /dev/null +++ b/tests/terrain_blender_material_validation.gd @@ -0,0 +1,190 @@ +extends SceneTree + +const REGION := preload("res://world/regions/starter_island_region.tscn") +const TERRAIN_MODEL_PATH: String = ( + "res://art/exported/environment/terrain/starter_island.glb" +) +const EXPECTED_SURFACES: Array[String] = [ + "grass_lite", + "dirt", + "cliff_wall", + "dirt_wall", + "sand", + "sandstone_heavy", +] + +var failures: Array[String] = [] + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + var model_scene := load(TERRAIN_MODEL_PATH) as PackedScene + _check(model_scene != null, "Starter-island GLB must load as a scene.") + if model_scene == null: + _finish() + return + + var model_root := model_scene.instantiate() + var model_terrain := _find_mesh(model_root, "starter_island") + _check(model_terrain != null, "GLB must contain the starter_island mesh.") + _check(_find_mesh(model_root, "Cube") == null, "GLB must not contain Cube.") + if model_terrain != null: + _validate_terrain_materials(model_terrain, "GLB") + model_root.free() + + var region := REGION.instantiate() + root.add_child(region) + await process_frame + var runtime_terrain := region.get_node_or_null( + "Terrain/Visual/starter_island" + ) as MeshInstance3D + _check(runtime_terrain != null, "Region must contain the terrain mesh.") + if runtime_terrain != null: + _validate_terrain_materials(runtime_terrain, "Region") + for surface_index: int in runtime_terrain.mesh.get_surface_count(): + _check( + runtime_terrain.get_surface_override_material(surface_index) == null, + "Region surface %d must use its Blender material." % surface_index, + ) + _validate_region_collision(region) + region.free() + _finish() + + +func _find_mesh(node: Node, mesh_name: String) -> MeshInstance3D: + if node is MeshInstance3D and node.name == mesh_name: + return node as MeshInstance3D + for child: Node in node.get_children(): + var match := _find_mesh(child, mesh_name) + if match != null: + return match + return null + + +func _validate_terrain_materials( + terrain: MeshInstance3D, + context: String, +) -> void: + _check(terrain.mesh != null, "%s terrain must have a mesh." % context) + if terrain.mesh == null: + return + _check( + terrain.mesh.get_surface_count() == EXPECTED_SURFACES.size(), + "%s terrain must have %d surfaces." % [ + context, + EXPECTED_SURFACES.size(), + ], + ) + var checked_count := mini( + terrain.mesh.get_surface_count(), + EXPECTED_SURFACES.size(), + ) + for surface_index: int in checked_count: + var expected_name := EXPECTED_SURFACES[surface_index] + _check( + terrain.mesh.surface_get_name(surface_index) == expected_name, + "%s surface %d must be named %s." % [ + context, + surface_index, + expected_name, + ], + ) + var material := terrain.get_active_material(surface_index) + _check( + material is StandardMaterial3D, + "%s surface %s must use a Blender material." % [ + context, + expected_name, + ], + ) + if material is StandardMaterial3D: + var texture := (material as StandardMaterial3D).albedo_texture + _check( + texture != null, + "%s surface %s must have an embedded texture." % [ + context, + expected_name, + ], + ) + if texture != null: + _check( + texture.resource_path.begins_with(TERRAIN_MODEL_PATH + "::"), + "%s surface %s must source its texture from the GLB." % [ + context, + expected_name, + ], + ) + + +func _validate_region_collision(region: StarterIslandRegion) -> void: + _check( + region.rebuild_terrain_collision_on_ready, + "Starter-island collision must rebuild from the imported GLB.", + ) + var visual_root := region.get_node_or_null("Terrain/Visual") as Node3D + var collision_shape := region.get_node_or_null( + "Terrain/Collision/Shape" + ) as CollisionShape3D + _check(visual_root != null, "Region must contain its visual hierarchy.") + _check(collision_shape != null, "Region must contain its collision owner.") + if visual_root == null or collision_shape == null: + return + var concave_shape := collision_shape.shape as ConcavePolygonShape3D + _check( + concave_shape != null, + "Region collision must be a generated concave polygon shape.", + ) + if concave_shape == null: + return + var expected_faces := PackedVector3Array() + var mesh_count := _append_collision_faces( + region, + visual_root, + expected_faces, + ) + var actual_faces := concave_shape.get_faces() + _check(mesh_count > 1, "Collision must include the imported prop meshes.") + _check(not expected_faces.is_empty(), "Imported meshes must provide collision.") + _check( + actual_faces == expected_faces, + "Generated collision must exactly match every imported mesh transform.", + ) + + +func _append_collision_faces( + region: StarterIslandRegion, + node: Node, + faces: PackedVector3Array, +) -> int: + var mesh_count := 0 + if node is MeshInstance3D: + var mesh_instance := node as MeshInstance3D + if mesh_instance.mesh != null: + var mesh_shape := mesh_instance.mesh.create_trimesh_shape() + if mesh_shape != null: + mesh_count += 1 + for face_vertex: Vector3 in mesh_shape.get_faces(): + faces.append( + region.to_local(mesh_instance.to_global(face_vertex)) + ) + for child: Node in node.get_children(): + mesh_count += _append_collision_faces(region, child, faces) + return mesh_count + + +func _check(condition: bool, message: String) -> void: + if not condition: + failures.append(message) + + +func _finish() -> void: + if failures.is_empty(): + print("Blender terrain and collision validation: PASS") + quit(0) + return + for failure: String in failures: + printerr("Blender terrain and collision validation: ", failure) + quit(1) diff --git a/tests/terrain_projection_validation.gd.uid b/tests/terrain_blender_material_validation.gd.uid similarity index 100% rename from tests/terrain_projection_validation.gd.uid rename to tests/terrain_blender_material_validation.gd.uid diff --git a/tests/terrain_projection_validation.gd b/tests/terrain_projection_validation.gd deleted file mode 100644 index 88a9999..0000000 --- a/tests/terrain_projection_validation.gd +++ /dev/null @@ -1,60 +0,0 @@ -extends SceneTree - -const REGION := preload("res://world/regions/starter_island_region.tscn") - - -func _initialize() -> void: - call_deferred("_run") - - -func _run() -> void: - var region := REGION.instantiate() - root.add_child(region) - await process_frame - var terrain := region.get_node( - "Terrain/Visual/starter_island" - ) as MeshInstance3D - assert(terrain != null) - assert(terrain.mesh.get_surface_count() == 5) - assert(terrain.mesh.surface_get_name(0) == "grass_lite") - assert(terrain.mesh.surface_get_name(1) == "dirt") - assert(terrain.mesh.surface_get_name(2) == "sand") - assert(terrain.mesh.surface_get_name(3) == "cliff_wall") - assert(terrain.mesh.surface_get_name(4) == "dirt_wall") - - _validate_projected_material(terrain.get_surface_override_material(0)) - _validate_projected_material(terrain.get_surface_override_material(2)) - for surface: int in [1, 3, 4]: - assert(terrain.get_surface_override_material(surface) == null) - assert(terrain.mesh.surface_get_material(surface) is StandardMaterial3D) - - print("Terrain projection validation: PASS") - region.free() - quit() - - -func _validate_projected_material(material: Material) -> void: - assert(material is ShaderMaterial) - var shader_material := material as ShaderMaterial - assert( - shader_material.shader.resource_path - == "res://world/materials/terrain_surface_projection.gdshader" - ) - assert( - is_equal_approx( - float(shader_material.get_shader_parameter("tile_world_size")), - 2.5 - ) - ) - assert( - is_equal_approx( - float(shader_material.get_shader_parameter("blend_sharpness")), - 6.0 - ) - ) - assert( - is_equal_approx( - float(shader_material.get_shader_parameter("top_projection_bias")), - 2.0 - ) - ) diff --git a/tests/texture_sampling_validation.gd b/tests/texture_sampling_validation.gd new file mode 100644 index 0000000..deeabce --- /dev/null +++ b/tests/texture_sampling_validation.gd @@ -0,0 +1,148 @@ +extends SceneTree + +const POLICY := preload("res://main/texture_sampling_policy.gd") + +var failures: Array[String] = [] + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + _validate_runtime_policy() + _validate_repository_files("res://") + if failures.is_empty(): + print("Texture sampling validation passed.") + quit(0) + return + for failure: String in failures: + printerr("Texture sampling validation: ", failure) + quit(1) + + +func _validate_runtime_policy() -> void: + var texture_rect := TextureRect.new() + texture_rect.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR + POLICY.enforce_node(texture_rect) + _check( + texture_rect.texture_filter == CanvasItem.TEXTURE_FILTER_NEAREST, + "CanvasItem policy must replace linear sampling with nearest sampling.", + ) + texture_rect.free() + + var sprite := Sprite3D.new() + sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR + POLICY.enforce_node(sprite) + _check( + sprite.texture_filter == BaseMaterial3D.TEXTURE_FILTER_NEAREST, + "Sprite3D policy must replace linear sampling with nearest sampling.", + ) + sprite.free() + + var material := StandardMaterial3D.new() + material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR + var quad := QuadMesh.new() + quad.material = material + var mesh_instance := MeshInstance3D.new() + mesh_instance.mesh = quad + POLICY.enforce_node(mesh_instance) + _check( + material.texture_filter == BaseMaterial3D.TEXTURE_FILTER_NEAREST, + "3D material policy must replace linear sampling with nearest sampling.", + ) + mesh_instance.free() + + +func _validate_repository_files(path: String) -> void: + var directory := DirAccess.open(path) + if directory == null: + failures.append("Could not scan %s." % path) + return + directory.list_dir_begin() + while true: + var name := directory.get_next() + if name.is_empty(): + break + if name == ".godot" or name == ".git": + continue + var child_path := path.path_join(name) + if directory.current_is_dir(): + _validate_repository_files(child_path) + continue + if name.ends_with(".import"): + _validate_import_file(child_path) + elif name.ends_with(".gdshader"): + _validate_shader_file(child_path) + elif name.ends_with(".tscn") or name.ends_with(".tres"): + _validate_serialized_resource(child_path) + elif name.ends_with(".gd") and child_path != get_script().resource_path: + _validate_script_file(child_path) + directory.list_dir_end() + + +func _validate_import_file(path: String) -> void: + var source := FileAccess.get_file_as_string(path) + _check( + "mipmaps/generate=true" not in source, + "Texture import generates mipmaps: %s" % path, + ) + + +func _validate_shader_file(path: String) -> void: + var source := FileAccess.get_file_as_string(path) + var declaration_start := source.find("uniform sampler") + while declaration_start >= 0: + var declaration_end := source.find(";", declaration_start) + if declaration_end < 0: + _check(false, "Incomplete sampler declaration in %s." % path) + return + var declaration := source.substr( + declaration_start, + declaration_end - declaration_start, + ) + _check( + "filter_nearest" in declaration, + "Shader sampler must declare filter_nearest: %s" % path, + ) + declaration_start = source.find("uniform sampler", declaration_end + 1) + + +func _validate_serialized_resource(path: String) -> void: + var source := FileAccess.get_file_as_string(path) + var uses_3d_filter_enum := false + for line: String in source.split("\n"): + if line.begins_with("["): + uses_3d_filter_enum = ( + ("type=\"Sprite3D\"" in line) + or ("type=\"AnimatedSprite3D\"" in line) + or ("type=\"StandardMaterial3D\"" in line) + or ("type=\"ORMMaterial3D\"" in line) + ) + if not line.begins_with("texture_filter = "): + continue + var value := int(line.trim_prefix("texture_filter = ")) + if uses_3d_filter_enum: + _check( + value == BaseMaterial3D.TEXTURE_FILTER_NEAREST, + "3D resource declares non-nearest filtering: %s" % path, + ) + else: + _check( + value == CanvasItem.TEXTURE_FILTER_PARENT_NODE + or value == CanvasItem.TEXTURE_FILTER_NEAREST, + "Canvas resource declares non-nearest filtering: %s" % path, + ) + + +func _validate_script_file(path: String) -> void: + var source := FileAccess.get_file_as_string(path) + _check( + "TEXTURE_FILTER_LINEAR" not in source, + "Script explicitly requests linear texture filtering: %s" % path, + ) + + +func _check(condition: bool, message: String) -> void: + if not condition: + failures.append(message) diff --git a/tests/texture_sampling_validation.gd.uid b/tests/texture_sampling_validation.gd.uid new file mode 100644 index 0000000..9a2f48b --- /dev/null +++ b/tests/texture_sampling_validation.gd.uid @@ -0,0 +1 @@ +uid://dqm0j1xnkeox1 diff --git a/tests/title_credits_validation.gd b/tests/title_credits_validation.gd new file mode 100644 index 0000000..7e50f89 --- /dev/null +++ b/tests/title_credits_validation.gd @@ -0,0 +1,207 @@ +extends SceneTree + +const TitleScreenScene := preload("res://ui/title_screen.tscn") +const TitleCreditsPageType = preload("res://ui/title_credits_page.gd") + +var _failures: Array[String] = [] + + +func _initialize() -> void: + _run.call_deferred() + + +func _run() -> void: + var original_size: Vector2i = root.size + root.size = Vector2i(1280, 720) + var title_screen := TitleScreenScene.instantiate() as TitleScreen + root.add_child(title_screen) + await process_frame + await process_frame + + var credits_button := title_screen.get_node( + "%CreditsButton" + ) as BubbleButton + var credits_page := title_screen.get_node( + "%CreditsPage" + ) as TitleCreditsPageType + var back_button: Button = credits_page.get_back_button() + var presentation := title_screen.get_node("%Center") as Control + var bubble_field := title_screen.get_node("%BubbleField") as Control + title_screen.call("_update_title_layout") + (title_screen.get_node("%ButtonCenter") as Control).show() + bubble_field.show() + title_screen.call("_set_title_bubbles_interactive", true) + await process_frame + + _expect(credits_button != null, "title menu exposes a credits button") + _expect(credits_page != null, "title screen contains the credits page") + _expect(not credits_page.visible, "credits page starts closed") + _expect( + Rect2(Vector2.ZERO, bubble_field.size).encloses( + Rect2(credits_button.position, credits_button.size) + ), + "credits bubble stays inside the authored title field", + ) + _expect( + credits_button.mouse_filter == Control.MOUSE_FILTER_STOP, + "credits bubble accepts pointer input", + ) + if OS.has_environment("NETFISHING_TITLE_MENU_CAPTURE"): + (title_screen.get_node("%StartPromptCenter") as Control).hide() + title_screen.set_process(true) + await create_timer(0.5).timeout + _expect( + _save_capture( + OS.get_environment("NETFISHING_TITLE_MENU_CAPTURE") + ) == OK, + "title menu validation capture saves successfully", + ) + title_screen.set_process(false) + + title_screen.set("_navigation_focus_active", false) + credits_button.pressed.emit() + await process_frame + _expect(credits_page.visible, "pointer activation opens credits") + _expect(not presentation.visible, "credits replace the primary title menu") + _expect( + root.gui_get_focus_owner() != back_button, + "pointer activation does not pseudo-select back", + ) + await create_timer(0.25).timeout + _expect( + credits_page.mouse_filter == Control.MOUSE_FILTER_PASS, + "credits page becomes pointer-interactive after entry motion", + ) + _expect( + back_button.mouse_filter == Control.MOUSE_FILTER_STOP, + "credits back button accepts pointer clicks", + ) + if OS.has_environment("NETFISHING_CREDITS_CAPTURE"): + await process_frame + _expect( + _save_capture( + OS.get_environment("NETFISHING_CREDITS_CAPTURE") + ) == OK, + "credits validation capture saves successfully", + ) + back_button.pressed.emit() + await process_frame + _expect(not credits_page.visible, "pointer back closes credits") + _expect(presentation.visible, "closing credits restores the title menu") + _expect( + root.gui_get_focus_owner() == null, + "pointer return leaves the title menu unfocused", + ) + + title_screen.set("_navigation_focus_active", true) + credits_button.grab_focus() + credits_button.pressed.emit() + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == back_button, + "keyboard or controller activation focuses the credits back button", + ) + + var pointer_motion := InputEventMouseMotion.new() + pointer_motion.position = Vector2(640.0, 360.0) + title_screen._input(pointer_motion) + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "pointer motion clears credits navigation focus", + ) + + var navigate := InputEventAction.new() + navigate.action = &"ui_down" + navigate.pressed = true + title_screen._input(navigate) + await process_frame + _expect( + root.gui_get_focus_owner() == back_button, + "navigation input restores credits focus", + ) + + var cancel := InputEventAction.new() + cancel.action = &"ui_cancel" + cancel.pressed = true + title_screen._input(cancel) + await process_frame + _expect(not credits_page.visible, "ui cancel closes credits") + _expect( + root.gui_get_focus_owner() == credits_button, + "navigation return restores focus to the credits bubble", + ) + + var chillnfill_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/ChillnfillName" + ) as Label + var voyager_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/VoyagerName" + ) as Label + var endeavour_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/EndeavourName" + ) as Label + var chillnfill_credit := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/ChillnfillCredit" + ) as Label + var tekgator_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/TekgatorName" + ) as Label + var tekgator_credit := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/TekgatorCredit" + ) as Label + var creative_credits := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits" + ) as VBoxContainer + _expect(voyager_name.text == "Voyager", "in-game credits use Voyager") + _expect( + endeavour_name.text == "Endeavour", + "in-game credits use Endeavour", + ) + _expect( + chillnfill_name.text == "chillnfill", + "in-game credits name chillnfill", + ) + _expect( + "trees and bridges" in chillnfill_credit.text.to_lower(), + "in-game credits describe the supplied model families", + ) + _expect( + tekgator_name.text == "Tekgator", + "in-game credits name Tekgator", + ) + _expect( + "shop icon" in tekgator_credit.text.to_lower(), + "in-game credits describe Tekgator's shop icon artwork", + ) + _expect( + Rect2(Vector2.ZERO, creative_credits.size).encloses( + Rect2(tekgator_credit.position, tekgator_credit.size) + ), + "Tekgator's credit stays inside the authored credits column", + ) + + title_screen.queue_free() + root.size = original_size + await process_frame + await process_frame + if _failures.is_empty(): + print("Title credits validation: PASS") + quit(0) + return + for failure: String in _failures: + push_error(failure) + quit(1) + + +func _expect(condition: bool, message: String) -> void: + if not condition: + _failures.append(message) + + +func _save_capture(path: String) -> Error: + var viewport_texture: Texture2D = root.get_texture() + if viewport_texture == null: + return ERR_UNAVAILABLE + return viewport_texture.get_image().save_png(path) diff --git a/tests/title_credits_validation.gd.uid b/tests/title_credits_validation.gd.uid new file mode 100644 index 0000000..747e70c --- /dev/null +++ b/tests/title_credits_validation.gd.uid @@ -0,0 +1 @@ +uid://dimq4ehtlw64n diff --git a/tests/ui_scaling_runtime_validation.gd b/tests/ui_scaling_runtime_validation.gd index f3f4974..f7144eb 100644 --- a/tests/ui_scaling_runtime_validation.gd +++ b/tests/ui_scaling_runtime_validation.gd @@ -94,7 +94,10 @@ func _run() -> void: assert(player_menu.size.is_equal_approx( UIReferencePresentationType.REFERENCE_SIZE )) - assert(hotbar.position.is_equal_approx(Vector2.ZERO)) + assert(hotbar.position.is_equal_approx(Vector2( + 0.0, + canonical_stage.position.y, + ))) assert(hotbar.size.is_equal_approx( UIReferencePresentationType.REFERENCE_SIZE )) @@ -202,6 +205,8 @@ func _run() -> void: presenter.queue_free() root.size = original_size - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout print("UI scaling runtime validation: PASS") quit() diff --git a/tests/world_time_multiplayer_validation.gd b/tests/world_time_multiplayer_validation.gd index 6aa89aa..cebb0eb 100644 --- a/tests/world_time_multiplayer_validation.gd +++ b/tests/world_time_multiplayer_validation.gd @@ -3,7 +3,7 @@ extends SceneTree const MainScene = preload("res://main/main.tscn") const TEST_PORT: int = 17983 const INITIAL_HOST_TIME: float = 19.75 -const UPDATED_HOST_TIME: float = 20.75 +const UPDATED_HOST_TIME: float = WorldTimeService.DUSK_END_HOUR const TIME_TOLERANCE_HOURS: float = 0.05 @@ -31,12 +31,18 @@ func _run_host() -> void: main.get_node("%WorldWeatherService") as WorldWeatherService ) assert(session.start_private_host(TEST_PORT)) + var game_ui := main.get_node("%GameUI") as CanvasLayer + var chat_ui := game_ui.get_node("%ChatUI") as ChatUI + assert(chat_ui.call("_handle_chat_command", "/weather clear")) + assert(world_weather.get_weather() == WorldWeatherService.Weather.SUNNY) world_time.synchronize_time(INITIAL_HOST_TIME) assert(is_equal_approx( world_time.get_persistent_time_hours(), INITIAL_HOST_TIME )) - world_weather.apply_authoritative_snapshot( - WorldWeatherService.Weather.RAINY, 300.0 + assert(chat_ui.call("_handle_chat_command", "/weather rainy")) + assert( + world_weather.get_persistent_weather() + == WorldWeatherService.Weather.RAINY ) assert(session.set_host_open(true)) @@ -55,15 +61,34 @@ func _run_host() -> void: assert(session.peer_supports_capability( remote_peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY )) + var remote_record: PeerRegistry.PeerRecord = session.get_peer_record( + remote_peer_id + ) + assert(remote_record != null and remote_record.identity_authenticated) + assert(session.set_peer_operator( + remote_peer_id, + remote_record.identity_fingerprint, + true, + )) assert(world_time.get_phase() == WorldTimeService.Phase.DUSK) - await create_timer(1.0).timeout - world_time.synchronize_time(UPDATED_HOST_TIME) + var command_deadline: int = Time.get_ticks_msec() + 10000 + while ( + Time.get_ticks_msec() < command_deadline + and _wrapped_time_difference( + world_time.get_time_hours(), UPDATED_HOST_TIME + ) > TIME_TOLERANCE_HOURS + ): + await process_frame assert(is_equal_approx( world_time.get_persistent_time_hours(), UPDATED_HOST_TIME )) - world_weather.apply_authoritative_snapshot( - WorldWeatherService.Weather.FOGGY, 300.0 + var fog_deadline: int = Time.get_ticks_msec() + 10000 + while Time.get_ticks_msec() < fog_deadline and not world_weather.is_foggy(): + await process_frame + assert( + world_weather.get_persistent_weather() + == WorldWeatherService.Weather.FOGGY ) var disconnect_deadline: int = Time.get_ticks_msec() + 12000 while ( @@ -75,7 +100,9 @@ func _run_host() -> void: print("World time multiplayer host validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() @@ -105,6 +132,10 @@ func _run_client() -> void: NetworkProtocol.WORLD_WEATHER_CAPABILITY )) assert(world_time.restore_persistent_time_hours(15.25)) + assert(world_weather.restore_persistent_state( + WorldWeatherService.Weather.CLOUDY, + 222.0, + )) var initial_deadline: int = Time.get_ticks_msec() + 8000 while ( @@ -139,6 +170,11 @@ func _run_client() -> void: assert(is_equal_approx(clock_panel.position.y, 10.0)) assert(is_equal_approx(weather_icon.position.y, 10.0)) assert(clock_panel.position.y + clock_panel.size.y < chat_panel.position.y) + var operator_deadline: int = Time.get_ticks_msec() + 10000 + while Time.get_ticks_msec() < operator_deadline and not session.is_local_operator(): + await process_frame + assert(session.is_local_operator()) + assert(chat_ui.call("_handle_chat_command", "/time night")) var update_deadline: int = Time.get_ticks_msec() + 10000 while ( @@ -154,6 +190,8 @@ func _run_client() -> void: assert(world_time.get_phase() == WorldTimeService.Phase.NIGHT) assert(is_equal_approx(world_time.get_persistent_time_hours(), 15.25)) assert(clock_label.text == world_time.get_clock_text()) + await create_timer(0.6).timeout + assert(chat_ui.call("_handle_chat_command", "/weather foggy")) var fog_deadline: int = Time.get_ticks_msec() + 8000 while ( Time.get_ticks_msec() < fog_deadline @@ -162,6 +200,14 @@ func _run_client() -> void: await process_frame assert(world_weather.is_foggy()) assert(weather_icon.get_weather() == WorldWeatherService.Weather.FOGGY) + assert( + world_weather.get_persistent_weather() + == WorldWeatherService.Weather.CLOUDY + ) + assert(is_equal_approx( + world_weather.get_persistent_seconds_remaining(), + 222.0, + )) chat_ui.call("set_dock_right", true) await process_frame assert(clock_panel.position.x > 1000.0) @@ -175,7 +221,9 @@ func _run_client() -> void: print("World time multiplayer client validation: PASS") session.disconnect_session("") main.queue_free() - await process_frame + for _frame: int in 4: + await process_frame + await create_timer(0.1).timeout quit() diff --git a/tests/world_time_validation.gd b/tests/world_time_validation.gd index 1908951..b043718 100644 --- a/tests/world_time_validation.gd +++ b/tests/world_time_validation.gd @@ -17,8 +17,12 @@ const FishableWaterRegionType = preload( const WeatherIconType = preload("res://ui/weather_icon.gd") const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres") - - +const SaltWaterMaterial: ShaderMaterial = preload( + "res://world/materials/stylized_water.tres" +) +const FreshWaterMaterial: ShaderMaterial = preload( + "res://world/materials/stylized_water_fresh.tres" +) func _initialize() -> void: call_deferred("_run") @@ -76,6 +80,20 @@ func _validate_clock_boundaries_and_duration() -> void: assert(is_equal_approx(clock.get_time_hours(), 8.0)) assert(not clock.is_night_period()) assert(clock.is_transition()) + + # Small high-refresh deltas must still advance the displayed minute. Using + # is_equal_approx() per frame used to suppress every clock notification. + var emitted_times: Array[float] = [] + clock.time_changed.connect( + func(time_hours: float, _phase: WorldTimeService.Phase) -> void: + emitted_times.append(time_hours) + ) + clock.begin_session(12.0 + 31.0 / 60.0) + emitted_times.clear() + for _frame: int in 1201: + clock.advance_time(1.0 / 240.0) + assert(clock.get_clock_text() == "12:33 pm") + assert(emitted_times.size() >= 2) clock.queue_free() @@ -124,7 +142,21 @@ func _validate_fishing_availability() -> void: assert(not night_fish.availability.is_available(day_context)) assert(night_fish.availability.is_available(night_context)) assert(night_fish.availability.is_available(transition_context)) - for fish_id: StringName in [&"bass", &"carp"]: + for fish_id: StringName in [ + &"bass", + &"carp", + &"tuna_albacore", + &"tuna_bigeye", + &"tuna_bluefin", + &"tuna_skipjack", + &"tuna_yellowfin", + &"goby_round", + &"salmon_atlantic", + &"salmon_chum", + &"salmon_coho", + &"salmon_pink", + &"salmon_sockeye", + ]: var all_time_fish: FishDataType = Catalog.get_fish_by_id(fish_id) assert(all_time_fish.availability.is_available(day_context)) assert(all_time_fish.availability.is_available(night_context)) @@ -215,6 +247,41 @@ func _validate_environment_presentation() -> void: < 0.01 ) var day_ambient_energy: float = runtime_environment.ambient_light_energy + assert(not runtime_environment.fog_enabled) + assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.35)) + var water_shader: Shader = preload( + "res://world/materials/stylized_water.gdshader" + ) + var sky_shader: Shader = preload( + "res://world/environment/netfishing_sky.gdshader" + ) + assert("uniform float fog_horizon_occlusion" in sky_shader.code) + assert("fog_horizon_color.rgb" in sky_shader.code) + assert("fog_disabled" in water_shader.code) + assert("surface_view_position = view_vertex.xyz" in water_shader.code) + assert("surface_view_distance = length(surface_view_position)" in water_shader.code) + assert( + "max(weather_fog_end, weather_fog_begin + 1.0),\n" + + "\t\tsurface_view_distance" + in water_shader.code + ) + assert("weather_fog_near_amount" in water_shader.code) + assert("float weather_fog_alpha = mix(" in water_shader.code) + assert("ALPHA = clamp(water_alpha, 0.1, weather_fog_alpha)" in water_shader.code) + assert(is_zero_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_amount") + ))) + assert(is_equal_approx(float( + SaltWaterMaterial.get_shader_parameter("surface_highlight_night_floor") + ), WorldTimeVisualController.DEFAULT_SURFACE_HIGHLIGHT_FLOOR)) + var day_water_brightness := float( + SaltWaterMaterial.get_shader_parameter("environment_brightness") + ) + assert(day_water_brightness > 0.99) + assert(is_equal_approx( + float(FreshWaterMaterial.get_shader_parameter("environment_brightness")), + day_water_brightness, + )) visuals.apply_time_immediately(0.0) var night_horizon: Color = runtime_sky_material.get_shader_parameter( "sky_horizon_color" @@ -237,7 +304,127 @@ func _validate_environment_presentation() -> void: assert(night_moon_direction.y > 0.85) assert(night_moon_direction.is_equal_approx(-night_sun_direction)) assert(runtime_environment.ambient_light_energy < day_ambient_energy) + assert(is_zero_approx(float( + runtime_sky_material.get_shader_parameter("fog_horizon_occlusion") + ))) + var night_water_brightness := float( + SaltWaterMaterial.get_shader_parameter("environment_brightness") + ) + assert(night_water_brightness < day_water_brightness * 0.4) + assert(is_equal_approx( + float(FreshWaterMaterial.get_shader_parameter("environment_brightness")), + night_water_brightness, + )) + var night_water_tint := ( + SaltWaterMaterial.get_shader_parameter("environment_tint") as Color + ) + assert(night_water_tint.get_luminance() < 0.5) + var night_background_energy := runtime_environment.background_energy_multiplier + var night_ambient_energy := runtime_environment.ambient_light_energy + var night_fog_light_energy := runtime_environment.fog_light_energy + var night_adjustment_brightness := runtime_environment.adjustment_brightness + var night_adjustment_saturation := runtime_environment.adjustment_saturation + visuals.apply_weather_immediately(WorldWeatherService.Weather.FOGGY) + visuals.apply_time_immediately(0.0) + assert(runtime_environment.fog_enabled) + assert(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_amount") + ) > 0.99) + assert(is_equal_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_near_amount") + ), WorldTimeVisualController.FOGGY_NIGHT_WATER_NEAR_FOG_AMOUNT)) + assert(is_equal_approx(float( + SaltWaterMaterial.get_shader_parameter("surface_highlight_night_floor") + ), WorldTimeVisualController.FOGGY_NIGHT_SURFACE_HIGHLIGHT_FLOOR)) + assert(is_equal_approx(float( + runtime_sky_material.get_shader_parameter("fog_horizon_occlusion") + ), 1.0)) + assert(( + runtime_sky_material.get_shader_parameter("fog_horizon_color") as Color + ).is_equal_approx(WorldTimeVisualController.NIGHT_FOG)) + var night_rendered_fog_color := ( + runtime_environment.fog_light_color.srgb_to_linear() + ) + night_rendered_fog_color.r *= runtime_environment.fog_light_energy + night_rendered_fog_color.g *= runtime_environment.fog_light_energy + night_rendered_fog_color.b *= runtime_environment.fog_light_energy + night_rendered_fog_color.a = 1.0 + night_rendered_fog_color = night_rendered_fog_color.linear_to_srgb() + var night_water_fog_color := ( + SaltWaterMaterial.get_shader_parameter("weather_fog_color") as Color + ) + var corrected_night_fog_color := night_rendered_fog_color + corrected_night_fog_color.r *= ( + WorldTimeVisualController.FOGGY_DARK_WATER_FOG_CORRECTION.r + ) + corrected_night_fog_color.g *= ( + WorldTimeVisualController.FOGGY_DARK_WATER_FOG_CORRECTION.g + ) + corrected_night_fog_color.b *= ( + WorldTimeVisualController.FOGGY_DARK_WATER_FOG_CORRECTION.b + ) + assert(night_water_fog_color.is_equal_approx(corrected_night_fog_color)) + var foggy_sky_horizon := ( + runtime_sky_material.get_shader_parameter("sky_horizon_color") as Color + ) + assert(foggy_sky_horizon.is_equal_approx(night_horizon)) + assert(is_equal_approx(runtime_environment.fog_sky_affect, 1.0)) + assert(is_zero_approx(runtime_environment.fog_aerial_perspective)) + assert(is_equal_approx(runtime_environment.fog_depth_begin, 3.0)) + assert(is_equal_approx(runtime_environment.fog_depth_end, 18.0)) + assert(is_equal_approx( + float(SaltWaterMaterial.get_shader_parameter("environment_brightness")), + night_water_brightness, + )) + assert(is_equal_approx( + runtime_environment.background_energy_multiplier, + night_background_energy, + )) + assert(is_equal_approx( + runtime_environment.ambient_light_energy, + night_ambient_energy, + )) + assert(is_equal_approx( + runtime_environment.fog_light_energy, + night_fog_light_energy, + )) + assert(is_equal_approx( + runtime_environment.adjustment_brightness, + night_adjustment_brightness, + )) + assert(is_equal_approx( + runtime_environment.adjustment_saturation, + night_adjustment_saturation, + )) + visuals.apply_time_immediately(8.0) + assert(is_equal_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_near_amount") + ), WorldTimeVisualController.FOGGY_NIGHT_WATER_NEAR_FOG_AMOUNT)) + assert(is_equal_approx(float( + runtime_sky_material.get_shader_parameter("fog_horizon_occlusion") + ), 1.0)) visuals.apply_time_immediately(20.0) + assert(is_zero_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_near_amount") + ))) + assert(is_zero_approx(float( + runtime_sky_material.get_shader_parameter("fog_horizon_occlusion") + ))) + visuals.apply_weather_immediately(WorldWeatherService.Weather.SUNNY) + visuals.apply_time_immediately(20.0) + assert(not runtime_environment.fog_enabled) + assert(is_zero_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_amount") + ))) + assert(is_zero_approx(float( + SaltWaterMaterial.get_shader_parameter("weather_fog_near_amount") + ))) + assert(is_equal_approx(float( + SaltWaterMaterial.get_shader_parameter("surface_highlight_night_floor") + ), WorldTimeVisualController.DEFAULT_SURFACE_HIGHLIGHT_FLOOR)) + assert(is_zero_approx(float( + runtime_sky_material.get_shader_parameter("fog_horizon_occlusion") + ))) var dusk_horizon: Color = runtime_sky_material.get_shader_parameter( "sky_horizon_color" ) as Color diff --git a/tests/world_weather_validation.gd b/tests/world_weather_validation.gd index 1238fc4..a725117 100644 --- a/tests/world_weather_validation.gd +++ b/tests/world_weather_validation.gd @@ -24,6 +24,8 @@ func _initialize() -> void: func _run() -> void: _validate_weather_scheduler() + _validate_authoritative_weather_override() + _validate_weather_persistence() _validate_snapshot_bounds() _validate_fishing_weather_seams() _validate_fishing_spot_context() @@ -73,6 +75,99 @@ func _duration_is_valid(weather: WorldWeatherServiceType) -> bool: return false +func _validate_authoritative_weather_override() -> void: + var weather := WorldWeatherServiceType.new() + root.add_child(weather) + assert(not weather.set_authoritative_weather( + WorldWeatherServiceType.Weather.FOGGY + )) + weather.begin_authoritative_session(20260812) + assert(weather.set_authoritative_weather( + WorldWeatherServiceType.Weather.FOGGY + )) + assert(weather.is_foggy()) + assert(_duration_is_valid(weather)) + weather.end_session() + assert(not weather.set_authoritative_weather( + WorldWeatherServiceType.Weather.RAINY + )) + + var clock := WorldTimeServiceType.new() + root.add_child(clock) + clock.begin_session(WorldTimeServiceType.DAY_START_HOUR) + var schedule: Array[Dictionary] = [] + for index: int in WorldWeatherServiceType.DAILY_PLAN_SEGMENT_COUNT: + schedule.append({ + "start_hour": fposmod( + WorldTimeServiceType.DAY_START_HOUR + + float(index) + * WorldWeatherServiceType.DAILY_PLAN_SEGMENT_HOURS, + WorldTimeServiceType.HOURS_PER_DAY, + ), + "weather": int(WorldWeatherServiceType.Weather.SUNNY), + }) + assert(weather.configure_daily_plan("command-test", schedule, clock)) + weather.begin_authoritative_session(20260812) + assert(weather.set_authoritative_weather( + WorldWeatherServiceType.Weather.RAINY + )) + weather.advance_weather(1.0) + assert(weather.is_raining()) + clock.synchronize_time( + WorldTimeServiceType.DAY_START_HOUR + + WorldWeatherServiceType.DAILY_PLAN_SEGMENT_HOURS + ) + weather.advance_weather(0.01) + assert(weather.get_weather() == WorldWeatherServiceType.Weather.SUNNY) + weather.queue_free() + clock.queue_free() + + +func _validate_weather_persistence() -> void: + var weather := WorldWeatherServiceType.new() + root.add_child(weather) + weather.set_persistence_tracking_enabled(true) + weather.begin_authoritative_session(20260802) + weather.apply_authoritative_snapshot( + WorldWeatherServiceType.Weather.RAINY, + 200.0, + ) + weather.advance_weather(12.5) + assert(weather.has_persistent_state()) + assert( + weather.get_persistent_weather() + == WorldWeatherServiceType.Weather.RAINY + ) + assert(is_equal_approx( + weather.get_persistent_seconds_remaining(), + 187.5, + )) + weather.set_persistence_tracking_enabled(false) + weather.begin_remote_session() + weather.apply_authoritative_snapshot( + WorldWeatherServiceType.Weather.FOGGY, + 45.0, + ) + assert(weather.is_foggy()) + assert( + weather.get_persistent_weather() + == WorldWeatherServiceType.Weather.RAINY + ) + assert(is_equal_approx( + weather.get_persistent_seconds_remaining(), + 187.5, + )) + weather.set_persistence_tracking_enabled(true) + weather.begin_authoritative_session(20260803) + assert(weather.is_raining()) + assert(is_equal_approx(weather.get_seconds_remaining(), 187.5)) + assert(not weather.restore_persistent_state( + WorldWeatherServiceType.Weather.RAINY, + WorldWeatherServiceType.MAX_PERSISTED_SECONDS + 1.0, + )) + weather.queue_free() + + func _validate_snapshot_bounds() -> void: assert(NetworkWorldWeatherServiceType.validate_snapshot({ "session_id": "session", @@ -166,29 +261,268 @@ func _validate_weather_presentation() -> void: weather.begin_remote_session() var rain_target := Node3D.new() world_root.add_child(rain_target) + var rain_camera := Camera3D.new() + world_root.add_child(rain_camera) + rain_target.global_position = Vector3(-18.0, 2.0, 14.0) + rain_camera.global_position = Vector3(24.0, 9.0, -18.0) var visuals := WorldTimeVisualControllerType.new() world_root.add_child(visuals) visuals.setup( - clock, world_environment, sun, weather, rain_target + clock, + world_environment, + sun, + weather, + rain_target, + func() -> Camera3D: return rain_camera, ) + var runtime_environment: Environment = world_environment.environment + var runtime_sky_material := ( + runtime_environment.sky.sky_material as ShaderMaterial + ) + var clear_background_energy: float = ( + runtime_environment.background_energy_multiplier + ) + var clear_ambient_energy: float = runtime_environment.ambient_light_energy + var clear_fog_light_energy: float = runtime_environment.fog_light_energy + var clear_brightness: float = runtime_environment.adjustment_brightness + var clear_saturation: float = runtime_environment.adjustment_saturation + var clear_sun_energy: float = sun.light_energy + var clear_water_brightness: float = float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "environment_brightness" + ) + ) + assert(not runtime_environment.fog_enabled) + assert(is_zero_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ))) + assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.35)) + assert(is_zero_approx(float( + runtime_sky_material.get_shader_parameter("cloud_opacity") + ))) + var storm_clouds := visuals.get_node("LocalStormClouds") as Node3D + assert(storm_clouds != null) + assert(storm_clouds.call("get_patch_count") == 81) + var cloud_field := storm_clouds.get_node("CloudField") as MultiMeshInstance3D + assert(cloud_field != null) + assert(cloud_field.multimesh != null) + assert(cloud_field.multimesh.instance_count == 81) + assert(cloud_field.multimesh.mesh is ArrayMesh) + var cloud_shader: Shader = preload( + "res://world/environment/local_storm_cloud.gdshader" + ) + assert("cull_disabled" in cloud_shader.code) + assert(LocalStormCloudLayer.WRAP_RADIUS >= 315.0) + assert(is_equal_approx(LocalStormCloudLayer.MAXIMUM_OPACITY, 1.0)) + assert( + LocalStormCloudLayer.DISTANCE_FADE_END + < LocalStormCloudLayer.WRAP_RADIUS + ) + assert(is_zero_approx(float(storm_clouds.call("get_storm_amount")))) visuals.apply_weather_immediately( WorldWeatherServiceType.Weather.FOGGY ) - var runtime_environment: Environment = world_environment.environment + assert(runtime_environment.fog_enabled) + assert(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ) > 0.99) + assert(is_zero_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_near_amount" + ) + ))) + var water_fog_color := ( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_color" + ) as Color + ) + var rendered_fog_color := ( + runtime_environment.fog_light_color.srgb_to_linear() + ) + rendered_fog_color.r *= runtime_environment.fog_light_energy + rendered_fog_color.g *= runtime_environment.fog_light_energy + rendered_fog_color.b *= runtime_environment.fog_light_energy + rendered_fog_color.a = 1.0 + rendered_fog_color = rendered_fog_color.linear_to_srgb() + assert(water_fog_color.is_equal_approx(rendered_fog_color)) + assert(is_equal_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "surface_highlight_night_floor" + ) + ), WorldTimeVisualControllerType.DEFAULT_SURFACE_HIGHLIGHT_FLOOR)) assert(runtime_environment.fog_depth_begin <= 4.01) - assert(runtime_environment.fog_depth_end <= 42.01) - assert(runtime_environment.fog_sky_affect >= 0.93) - assert(runtime_environment.fog_aerial_perspective >= 0.91) - assert(runtime_environment.adjustment_saturation < 0.70) + assert(is_equal_approx(runtime_environment.fog_depth_end, 18.0)) + assert(is_equal_approx(runtime_environment.fog_sky_affect, 1.0)) + assert(is_zero_approx(runtime_environment.fog_aerial_perspective)) + assert(is_zero_approx(float( + runtime_sky_material.get_shader_parameter("cloud_opacity") + ))) + assert(is_equal_approx( + runtime_environment.background_energy_multiplier, + clear_background_energy + * WorldTimeVisualControllerType.FOG_DAYLIGHT_SCENE_BRIGHTNESS, + )) + assert(is_equal_approx( + runtime_environment.ambient_light_energy, + clear_ambient_energy + * WorldTimeVisualControllerType.FOG_DAYLIGHT_SCENE_BRIGHTNESS, + )) + assert(is_equal_approx( + runtime_environment.fog_light_energy, + clear_fog_light_energy + * WorldTimeVisualControllerType.FOG_DAYLIGHT_FOG_LIGHT_BRIGHTNESS, + )) + assert(is_equal_approx( + runtime_environment.adjustment_brightness, + clear_brightness + * WorldTimeVisualControllerType.FOG_DAYLIGHT_POST_BRIGHTNESS, + )) + assert(is_equal_approx(sun.light_energy, clear_sun_energy * 0.30)) + assert(is_equal_approx( + float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "environment_brightness" + ) + ), + clear_water_brightness + * WorldTimeVisualControllerType.FOG_DAYLIGHT_WATER_BRIGHTNESS, + )) + assert(is_equal_approx( + runtime_environment.adjustment_saturation, + clear_saturation, + )) visuals.apply_weather_immediately( WorldWeatherServiceType.Weather.RAINY ) + assert(runtime_environment.fog_enabled) + assert(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ) > 0.99) + assert(is_zero_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_near_amount" + ) + ))) + var rainy_water_fog_color := ( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_color" + ) as Color + ) + assert( + rainy_water_fog_color.is_equal_approx( + WorldTimeVisualControllerType.RAIN_WATER_FOG_COLOR + ) + ) + assert(is_equal_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "surface_highlight_night_floor" + ) + ), WorldTimeVisualControllerType.DEFAULT_SURFACE_HIGHLIGHT_FLOOR)) + assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.68)) + assert( + float(runtime_sky_material.get_shader_parameter("cloud_coverage")) + > 0.98 + ) + assert( + float(runtime_sky_material.get_shader_parameter("cloud_opacity")) + > 0.99 + ) + assert(float(storm_clouds.call("get_storm_amount")) > 0.87) + assert(storm_clouds.visible) + assert(is_equal_approx( + runtime_environment.adjustment_brightness, + clear_brightness + * 0.92 + * WorldTimeVisualControllerType.RAIN_DAYLIGHT_POST_BRIGHTNESS, + )) var rain := visuals.get_node("LocalRain") as GPUParticles3D assert(rain != null) + assert(rain.global_position.is_equal_approx( + rain_camera.global_position + + WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET + )) + rain_camera.global_position = Vector3(-31.0, 15.0, 42.0) + visuals.call("_update_rain_position") + assert(rain.global_position.is_equal_approx( + rain_camera.global_position + + WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET + )) assert(rain.emitting) assert(rain.amount_ratio > 0.99) + assert(rain.amount == WorldTimeVisualControllerType.RAIN_PARTICLE_AMOUNT) + assert(rain.visibility_aabb == ( + WorldTimeVisualControllerType.RAIN_VISIBILITY_AABB + )) + var rain_material := rain.process_material as ParticleProcessMaterial + assert(rain_material != null) + assert(rain_material.emission_box_extents.is_equal_approx( + WorldTimeVisualControllerType.RAIN_EMISSION_EXTENTS + )) + assert(is_equal_approx( + rain_material.initial_velocity_min, + WorldTimeVisualControllerType.RAIN_VELOCITY_MIN, + )) + assert(is_equal_approx( + rain_material.initial_velocity_max, + WorldTimeVisualControllerType.RAIN_VELOCITY_MAX, + )) + var rain_mesh := rain.draw_pass_1 as BoxMesh + assert(rain_mesh != null) + assert(rain_mesh.size.is_equal_approx( + WorldTimeVisualControllerType.RAIN_DROP_SIZE + )) + visuals.call( + "_on_weather_changed", + WorldWeatherServiceType.Weather.SUNNY, + 60.0, + ) + visuals.call( + "_process", + WorldTimeVisualControllerType.WEATHER_TRANSITION_SECONDS * 0.5, + ) + var halfway_water_fog: float = float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ) + assert(halfway_water_fog > 0.49 and halfway_water_fog < 0.51) + assert(is_equal_approx( + float( + WorldTimeVisualControllerType.FRESH_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ), + halfway_water_fog, + )) + assert(runtime_environment.fog_enabled) + visuals.call( + "_process", + WorldTimeVisualControllerType.WEATHER_TRANSITION_SECONDS * 0.5, + ) + assert(is_zero_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ))) + assert(not runtime_environment.fog_enabled) + visuals.apply_weather_immediately( + WorldWeatherServiceType.Weather.CLOUDY + ) + assert(not runtime_environment.fog_enabled) + assert(is_zero_approx(float( + WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter( + "weather_fog_amount" + ) + ))) visuals.apply_weather_immediately( WorldWeatherServiceType.Weather.SUNNY ) + assert(not runtime_environment.fog_enabled) assert(not rain.emitting) world_root.queue_free() diff --git a/ui/animalese_voice.gd b/ui/animalese_voice.gd new file mode 100644 index 0000000..9a75434 --- /dev/null +++ b/ui/animalese_voice.gd @@ -0,0 +1,125 @@ +class_name AnimaleseVoice +extends Node + +const SAMPLE_DIRECTORY := "res://sound/dialogue/animalese/placeholder" +const SUPPORTED_CHARACTERS := "abcdefghijklmnopqrstuvwxyz" +const DEFAULT_CHARACTERS_PER_SECOND: float = 28.0 +const POLYPHONY: int = 8 +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) +const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd") + +var base_pitch: float = 1.0 +var volume_db: float = -13.0 + +var _samples: Dictionary[String, AudioStream] = {} +var _player: AudioStreamPlayer +var _playback: AudioStreamPlaybackPolyphonic + + +func _ready() -> void: + _load_samples() + var polyphonic_stream := AudioStreamPolyphonic.new() + polyphonic_stream.polyphony = POLYPHONY + _player = AudioStreamPlayer.new() + _player.name = "AnimaleseAudio" + _player.bus = &"SFX" + _player.stream = polyphonic_stream + add_child(_player) + _player.play() + _playback = ( + _player.get_stream_playback() as AudioStreamPlaybackPolyphonic + ) + + +func speak_text( + tween_owner: Node, + text: String, + voice_key: String, + voice_profile_id: String = VoiceProfilesType.DEFAULT_ID, + characters_per_second: float = -1.0, +) -> Tween: + var speech_tween := tween_owner.create_tween() + var resolved_characters_per_second := ( + characters_per_second + if characters_per_second > 0.0 + else TypewriterRevealType.get_characters_per_second() + ) + var character_seconds := 1.0 / maxf( + resolved_characters_per_second, + 1.0, + ) + var voice_pitch := ( + base_pitch * VoiceProfilesType.pitch_for(voice_profile_id) + ) + for character_index: int in range(text.length()): + speech_tween.tween_interval(character_seconds) + speech_tween.tween_callback( + _play_character.bind( + text.substr(character_index, 1), + character_index, + text, + voice_key, + voice_pitch, + ) + ) + return speech_tween + + +func _load_samples() -> void: + for character_index: int in range(SUPPORTED_CHARACTERS.length()): + var character := SUPPORTED_CHARACTERS.substr(character_index, 1) + _load_sample(character) + _load_sample("fallback") + + +func _load_sample(sample_name: String) -> void: + var sample_path := "%s/%s.wav" % [SAMPLE_DIRECTORY, sample_name] + var sample := load(sample_path) as AudioStream + if sample != null: + _samples[sample_name] = sample + + +func _play_character( + character: String, + character_index: int, + full_text: String, + voice_key: String, + voice_pitch: float, +) -> void: + if _playback == null or character.strip_edges().is_empty(): + return + var normalized := character.to_lower() + if normalized in [".", ",", "!", "?", ":", ";", "-", "_"]: + return + var sample_name := ( + normalized if SUPPORTED_CHARACTERS.contains(normalized) else "fallback" + ) + var sample: AudioStream = _samples.get(sample_name) + if sample == null: + return + var speaker_variation := ( + float(posmod(hash(voice_key), 1001)) / 1000.0 - 0.5 + ) * 0.16 + var character_variation := ( + float(posmod(hash("%s:%d" % [voice_key, character_index]), 1001)) + / 1000.0 + - 0.5 + ) * 0.10 + var question_lift := 0.0 + var question_lift_start: int = floori(float(full_text.length()) * 0.75) + if full_text.ends_with("?") and character_index >= question_lift_start: + var final_progress := ( + float(character_index) / maxf(float(full_text.length() - 1), 1.0) + ) + question_lift = lerpf(0.0, 0.14, final_progress) + var pitch := clampf( + voice_pitch + + speaker_variation + + character_variation + + question_lift, + 0.72, + 1.45, + ) + _playback.play_stream(sample, 0.0, volume_db, pitch) diff --git a/ui/animalese_voice.gd.uid b/ui/animalese_voice.gd.uid new file mode 100644 index 0000000..4cfdf9b --- /dev/null +++ b/ui/animalese_voice.gd.uid @@ -0,0 +1 @@ +uid://djst00wf8yntj diff --git a/ui/art_kit_marker_icon.gdshader b/ui/art_kit_marker_icon.gdshader new file mode 100644 index 0000000..79a6564 --- /dev/null +++ b/ui/art_kit_marker_icon.gdshader @@ -0,0 +1,15 @@ +shader_type canvas_item; + +uniform vec4 marker_color : source_color = vec4(1.0, 0.0, 0.0, 1.0); + + +void fragment() { + vec4 source = texture(TEXTURE, UV) * COLOR; + float red_dominance = source.r - max(source.g, source.b); + float marker_mask = smoothstep(0.04, 0.18, red_dominance); + vec3 colored_marker = marker_color.rgb * source.r; + COLOR = vec4( + mix(source.rgb, colored_marker, marker_mask), + source.a * marker_color.a + ); +} diff --git a/ui/art_kit_marker_icon.gdshader.uid b/ui/art_kit_marker_icon.gdshader.uid new file mode 100644 index 0000000..0e7148f --- /dev/null +++ b/ui/art_kit_marker_icon.gdshader.uid @@ -0,0 +1 @@ +uid://bsg3cwrnjq6ev diff --git a/ui/assets/logbook/ui_logbook.png b/ui/assets/logbook/ui_logbook.png new file mode 100644 index 0000000..2e6126d Binary files /dev/null and b/ui/assets/logbook/ui_logbook.png differ diff --git a/ui/assets/logbook/ui_logbook.png.import b/ui/assets/logbook/ui_logbook.png.import new file mode 100644 index 0000000..1ffd643 --- /dev/null +++ b/ui/assets/logbook/ui_logbook.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnwbcopsjmi3s" +path="res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/logbook/ui_logbook.png" +dest_files=["res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/chat_ui.gd b/ui/chat_ui.gd index 9167a18..c080a04 100644 --- a/ui/chat_ui.gd +++ b/ui/chat_ui.gd @@ -6,30 +6,65 @@ const RECENT_SECONDS: float = 8.0 const SPEECH_SECONDS: float = 6.0 const DRAFT_SAVE_DELAY: float = 0.4 const IDLE_ALPHA: float = 0.58 -const ORIGINAL_PANEL_WIDTH: float = 390.0 -const PANEL_WIDTH: float = ORIGINAL_PANEL_WIDTH * 0.85 +const CHAT_SURFACE_COLOR := Color(0.025, 0.13, 0.19, 0.94) +const PANEL_WIDTH: float = 290.0 const COMPACT_HEIGHT: float = 250.0 -const BOTTOM_MARGIN: float = 94.0 +const BOTTOM_MARGIN: float = 12.0 +const EXPANDED_TOP_MARGIN: float = 94.0 const MIN_TOP_MARGIN: float = 24.0 const MIN_HISTORY_HEIGHT: float = 92.0 -const HISTORY_FONT_SIZE: int = 17 +const HISTORY_FONT_SIZE: int = 20 const INPUT_FONT_SIZE: int = 23 const HINT_FONT_SIZE: int = 12 const HISTORY_INPUT_GAP: int = 8 const HANDLE_SIZE := Vector2(34, 42) const HANDLE_GAP: float = 6.0 +const HANDLE_TOP_INSET: float = 18.0 const COLLAPSED_REVEAL_WIDTH: float = 8.0 const HINT_EDGE_MARGIN: float = 4.0 -const CLOCK_SIZE := Vector2(116.0, 34.0) +const SPEECH_BUBBLE_WIDTH: float = 320.0 +const SPEECH_POINTER_HALF_WIDTH: float = 12.0 +const SPEECH_POINTER_HEIGHT: float = 14.0 +const SPEECH_POINTER_OVERLAP: float = 3.0 +const MOBILE_COMPACT_WIDTH: float = 620.0 +const MOBILE_EXPANDED_WIDTH: float = 820.0 +const MOBILE_COMPACT_HEIGHT: float = 220.0 +const MOBILE_EXPANDED_HEIGHT: float = 390.0 +const MOBILE_EDGE_MARGIN: float = 12.0 +const CLOCK_SIZE := Vector2(174.0, 51.0) +const CLOCK_FONT_SIZE: int = 27 const CLOCK_EDGE_MARGIN: float = 10.0 -const WEATHER_ICON_SIZE := Vector2(34.0, 34.0) +const WEATHER_ICON_SIZE := Vector2(51.0, 51.0) const WEATHER_ICON_GAP: float = 6.0 +const FISH_FINDER_EFFECT_ICON_SIZE := Vector2(18.0, 18.0) +const FISH_FINDER_EFFECT_ICON: Texture2D = preload( + "res://items/icons/consumables/64_consumable_fishfinder.png" +) +const CHAT_SHOW_ICON: Texture2D = preload( + "res://ui/icons/pictograms/arrow_light_right_more.png" +) +const CHAT_HIDE_ICON: Texture2D = preload( + "res://ui/icons/pictograms/arrow_light_left_more.png" +) +const CHAT_EXPAND_ICON: Texture2D = preload( + "res://ui/icons/pictograms/arrow_light_up_more.png" +) +const CHAT_COMPACT_ICON: Texture2D = preload( + "res://ui/icons/pictograms/arrow_light_down_more.png" +) const WorldTimeServiceType = preload("res://world/world_time_service.gd") const WorldWeatherServiceType = preload( "res://world/world_weather_service.gd" ) const WeatherIconType = preload("res://ui/weather_icon.gd") +const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd") +const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd") +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) + + enum PresentationState { COLLAPSED, COMPACT, @@ -53,15 +88,18 @@ var _height_button: Button var _unread_indicator: Label var _hint: Label var _speech_layer: Control +var _animalese_voice: AnimaleseVoiceType var _clock_panel: PanelContainer var _clock_label: Label var _weather_icon: WeatherIconType +var _fish_finder_effect_icon: TextureRect var _speech: Dictionary[int, Dictionary] = {} var _draft_save_timer: Timer var _opacity_tween: Tween var _height_tween: Tween var _opened: bool = false var _available: bool = false +var _hud_hidden: bool = false var _presentation_state := PresentationState.COMPACT var _visible_state_before_collapse := PresentationState.COMPACT var _panel_hovered: bool = false @@ -70,12 +108,15 @@ var _last_message_time: float = -INF var _target_alpha: float = -1.0 var _send_pending: bool = false var _pending_send_body: String = "" -var _prior_movement: bool = true -var _prior_camera: bool = true +var _controller_refocused: bool = false +var _input_lock_applied: bool = false +var _last_submit_frame: int = -1 var _output_scale: float = 1.0 var _dock_right: bool = false +var _mobile_mode: bool = false var _world_time: WorldTimeServiceType var _world_weather: WorldWeatherServiceType +var _item_effects: PlayerItemEffects func _ready() -> void: @@ -95,6 +136,7 @@ func setup( settings: PlayerSettingsManager, world_time: WorldTimeServiceType, world_weather: WorldWeatherServiceType, + item_effects: PlayerItemEffects, ) -> void: _service = service _session = session @@ -104,6 +146,14 @@ func setup( _settings = settings _world_time = world_time _world_weather = world_weather + _item_effects = item_effects + if ( + _fishing_spot != null + and not _fishing_spot.local_speech_requested.is_connected( + show_local_speech + ) + ): + _fishing_spot.local_speech_requested.connect(show_local_speech) if ( _world_time != null and not _world_time.time_changed.is_connected( @@ -125,11 +175,13 @@ func setup( _world_weather.get_weather(), _world_weather.get_seconds_remaining(), ) + set_mobile_mode(_settings.current_settings.chat_mobile_mode) set_dock_right(_settings.current_settings.chat_dock_right) _service.message_received.connect(_on_message) _service.local_message_confirmed.connect(_on_local_message_confirmed) _service.history_replaced.connect(_on_history) _service.send_rejected.connect(_on_rejected) + _service.world_command_finished.connect(_on_world_command_finished) _session.peer_removed.connect(_on_peer_removed) _entry.text = _settings.current_settings.chat_draft _entry.caret_column = _entry.text.length() @@ -151,23 +203,33 @@ func open_chat() -> void: return if _presentation_state == PresentationState.COLLAPSED: _set_presentation_state(_visible_state_before_collapse, true, true) + _controller_refocused = false _opened = true text_entry_ownership_changed.emit(true) - _prior_movement = _player.is_movement_enabled() - _prior_camera = _player.is_camera_input_enabled() - _player.set_movement_enabled(false) - _player.set_camera_input_enabled(false) + _input_lock_applied = true + _player.set_local_input_suppressed(INPUT_OWNER, true) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) # Clear the last authoritative input immediately. Merely disabling local # prediction would let a remote host continue the last held movement. _session.submit_neutral_local_movement() _entry.show() - _entry.grab_focus() + _entry.virtual_keyboard_enabled = false _hint.hide() + _refresh_input_ownership() + call_deferred("_focus_entry_after_open") _update_panel_opacity(true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE +func open_command_chat() -> void: + open_chat() + if not _opened: + return + if not _entry.text.begins_with("/"): + _entry.text = "/" + _entry.text + _entry.caret_column = _entry.text.length() + + func set_available(value: bool) -> void: _available = value if not value: @@ -180,33 +242,116 @@ func set_available(value: bool) -> void: func close_chat() -> void: - if not _opened: - return + var ownership_was_active: bool = _opened or _input_lock_applied _opened = false - text_entry_ownership_changed.emit(false) + _set_status("") + _entry.virtual_keyboard_enabled = false _entry.release_focus() _entry.hide() - _player.set_movement_enabled(_prior_movement) - _player.set_camera_input_enabled(_prior_camera) - _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) + if _input_lock_applied: + _player.set_local_input_suppressed(INPUT_OWNER, false) + _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) + _input_lock_applied = false + if ownership_was_active: + text_entry_ownership_changed.emit(false) _flush_draft() _refresh_visibility() + _refresh_input_ownership() + + +func toggle_chat() -> void: + if _presentation_state == PresentationState.COLLAPSED: + open_chat() + return + if _opened: + close_chat() + _set_presentation_state(PresentationState.COLLAPSED, true, true) + return + if _controller_refocused: + _controller_refocused = false + _set_presentation_state(PresentationState.COLLAPSED, true, true) + return + open_chat() + + +func refocus_gameplay() -> void: + close_chat() + _controller_refocused = true + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() + + +func toggle_focus() -> void: + if _presentation_state == PresentationState.COLLAPSED: + return + if _opened: + refocus_gameplay() + else: + open_chat() + + +func request_virtual_keyboard() -> bool: + if not _opened or not _entry.has_focus(): + return false + _entry.virtual_keyboard_enabled = true + # Re-entering focus after the controller accept event is the explicit + # request Android uses to display its keyboard. Merely focusing Chat keeps + # the keyboard disabled so the world remains readable. + _entry.release_focus() + _entry.call_deferred("grab_focus") + return true + + +func _focus_entry_after_open() -> void: + if not _opened or not _entry.visible: + return + _entry.grab_focus() + _refresh_input_ownership() + + +func is_open() -> bool: + return _opened + + +func is_collapsed() -> bool: + return _presentation_state == PresentationState.COLLAPSED func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("open_chat"): + if event is InputEventKey: + if _opened: + _focus_entry_after_open() + else: + open_chat() + else: + toggle_chat() + get_viewport().set_input_as_handled() + return if event is InputEventKey and event.pressed and not event.echo: if event.keycode in [KEY_ENTER, KEY_KP_ENTER]: if _opened: + _last_submit_frame = Engine.get_process_frames() _send() elif ( _available and get_viewport().gui_get_focus_owner() is not LineEdit + and Engine.get_process_frames() != _last_submit_frame ): open_chat() get_viewport().set_input_as_handled() elif event.keycode == KEY_T and not _opened and _available: open_chat() get_viewport().set_input_as_handled() + elif ( + event.unicode == 47 + and not _opened + and _available + and get_viewport().gui_get_focus_owner() is not LineEdit + ): + open_command_chat() + get_viewport().set_input_as_handled() elif event.keycode == KEY_ESCAPE and _opened: close_chat() get_viewport().set_input_as_handled() @@ -219,6 +364,9 @@ func _process(_delta: float) -> void: func _exit_tree() -> void: + if _input_lock_applied and is_instance_valid(_player): + _player.set_local_input_suppressed(INPUT_OWNER, false) + _input_lock_applied = false _flush_draft() @@ -238,11 +386,20 @@ func _build_ui() -> void: _clock_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _clock_label.mouse_filter = Control.MOUSE_FILTER_IGNORE _clock_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) - _clock_label.add_theme_font_size_override("font_size", 18) + _clock_label.add_theme_font_size_override("font_size", CLOCK_FONT_SIZE) _clock_label.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY ) _clock_panel.add_child(_clock_label) + _fish_finder_effect_icon = TextureRect.new() + _fish_finder_effect_icon.name = "FishFinderEffectIcon" + _fish_finder_effect_icon.texture = FISH_FINDER_EFFECT_ICON + _fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE + _fish_finder_effect_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + _fish_finder_effect_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + _fish_finder_effect_icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + _fish_finder_effect_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(_fish_finder_effect_icon) _weather_icon = WeatherIconType.new() _weather_icon.name = "WorldWeatherIcon" _weather_icon.size = WEATHER_ICON_SIZE @@ -271,7 +428,7 @@ func _build_ui() -> void: _history.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _history.fit_content = false _history.scroll_active = true - _history.scroll_following = false + _history.scroll_following = true _history.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM _history.mouse_filter = Control.MOUSE_FILTER_STOP _history.add_theme_font_override("normal_font", UtilityPageStyle.TuffyFont) @@ -280,7 +437,10 @@ func _build_ui() -> void: _history.add_theme_stylebox_override("normal", _borderless_style(Color.TRANSPARENT)) box.add_child(_history) _status = Label.new() + _status.name = "ChatStatus" _status.custom_minimum_size = Vector2(0, 20) + _status.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _status.hide() _status.mouse_filter = Control.MOUSE_FILTER_IGNORE _status.add_theme_font_override("font", UtilityPageStyle.TuffyFont) @@ -290,7 +450,7 @@ func _build_ui() -> void: _entry.name = "ChatEntry" _entry.placeholder_text = "type a message…" _entry.max_length = NetworkChatProtocol.MAX_VISIBLE_CHARACTERS - _entry.text_submitted.connect(func(_value: String) -> void: _send()) + _entry.text_submitted.connect(_on_entry_text_submitted) _entry.text_changed.connect(_on_draft_changed) UtilityPageStyle.apply_ocean_line_edit(_entry) _entry.add_theme_font_size_override("font_size", INPUT_FONT_SIZE) @@ -302,6 +462,15 @@ func _build_ui() -> void: _collapse_button.mouse_filter = Control.MOUSE_FILTER_STOP _collapse_button.focus_mode = Control.FOCUS_ALL _collapse_button.z_index = 3 + _collapse_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + _collapse_button.expand_icon = true + _collapse_button.alignment = HORIZONTAL_ALIGNMENT_CENTER + _collapse_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + _collapse_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + _collapse_button.add_theme_constant_override( + "icon_max_width", + roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5), + ) _collapse_button.tooltip_text = "Collapse chat" _collapse_button.pressed.connect(func() -> void: if _presentation_state == PresentationState.COLLAPSED: @@ -328,6 +497,15 @@ func _build_ui() -> void: _height_button.mouse_filter = Control.MOUSE_FILTER_STOP _height_button.focus_mode = Control.FOCUS_ALL _height_button.z_index = 3 + _height_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + _height_button.expand_icon = true + _height_button.alignment = HORIZONTAL_ALIGNMENT_CENTER + _height_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + _height_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + _height_button.add_theme_constant_override( + "icon_max_width", + roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5), + ) _height_button.pressed.connect(func() -> void: if _presentation_state == PresentationState.COLLAPSED: _visible_state_before_collapse = ( @@ -368,7 +546,6 @@ func _build_ui() -> void: add_child(_unread_indicator) _hint = Label.new() _hint.name = "ChatHint" - _hint.text = "Press Enter to chat" _hint.size = Vector2(180, 18) _hint.mouse_filter = Control.MOUSE_FILTER_IGNORE _hint.add_theme_font_override("font", UtilityPageStyle.TuffyFont) @@ -384,6 +561,9 @@ func _build_ui() -> void: _speech_layer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _speech_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_speech_layer) + _animalese_voice = AnimaleseVoiceType.new() + _animalese_voice.name = "PlayerAnimaleseVoice" + add_child(_animalese_voice) _draft_save_timer = Timer.new() _draft_save_timer.one_shot = true _draft_save_timer.wait_time = DRAFT_SAVE_DELAY @@ -397,14 +577,21 @@ func _build_ui() -> void: _unread_indicator.hide() _hint.hide() _layout_presentation(false) + _refresh_input_ownership() func _chat_panel_style() -> StyleBoxFlat: - var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94)) - style.corner_radius_top_left = 10 if _dock_right else 0 - style.corner_radius_bottom_left = 10 if _dock_right else 0 - style.corner_radius_top_right = 0 if _dock_right else 10 - style.corner_radius_bottom_right = 0 if _dock_right else 10 + var style := _borderless_style(CHAT_SURFACE_COLOR) + if _mobile_mode: + style.corner_radius_top_left = 0 + style.corner_radius_top_right = 0 + style.corner_radius_bottom_left = 10 + style.corner_radius_bottom_right = 10 + else: + style.corner_radius_top_left = 10 if _dock_right else 0 + style.corner_radius_bottom_left = 10 if _dock_right else 0 + style.corner_radius_top_right = 0 if _dock_right else 10 + style.corner_radius_bottom_right = 0 if _dock_right else 10 style.content_margin_left = 12 style.content_margin_right = 12 style.content_margin_top = 10 @@ -412,8 +599,35 @@ func _chat_panel_style() -> StyleBoxFlat: return style +func _refresh_input_ownership() -> void: + if _panel == null or _history == null: + return + var filter: Control.MouseFilter = ( + Control.MOUSE_FILTER_STOP + if _opened + else Control.MOUSE_FILTER_IGNORE + ) + _panel.mouse_filter = filter + _history.mouse_filter = filter + _entry.focus_mode = ( + Control.FOCUS_ALL if _opened else Control.FOCUS_NONE + ) + _collapse_button.focus_mode = ( + Control.FOCUS_ALL if _opened else Control.FOCUS_NONE + ) + _height_button.focus_mode = ( + Control.FOCUS_ALL + if _opened and not _mobile_mode + else Control.FOCUS_NONE + ) + if not _opened: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner in [_entry, _collapse_button, _height_button]: + get_viewport().gui_release_focus() + + func _clock_panel_style() -> StyleBoxFlat: - var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94)) + var style := _borderless_style(CHAT_SURFACE_COLOR) style.set_corner_radius_all(12) style.content_margin_left = 10 style.content_margin_right = 10 @@ -426,6 +640,10 @@ func _borderless_style(color: Color) -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = color style.set_border_width_all(0) + style.anti_aliasing = false + style.shadow_size = 0 + style.shadow_color = Color.TRANSPARENT + style.shadow_offset = Vector2.ZERO return style @@ -434,6 +652,12 @@ func _flat_chat_button_style(color: Color) -> StyleBoxFlat: style.bg_color = color style.set_border_width_all(0) style.set_corner_radius_all(0) + if _dock_right: + style.corner_radius_top_left = 10 + style.corner_radius_bottom_left = 10 + else: + style.corner_radius_top_right = 10 + style.corner_radius_bottom_right = 10 style.anti_aliasing = false style.shadow_size = 0 style.shadow_color = Color.TRANSPARENT @@ -476,28 +700,61 @@ func _apply_flat_chat_button(button: Button) -> void: button.add_theme_stylebox_override( "disabled", _flat_chat_button_style(UtilityPageStyle.OCEAN_DISABLED) ) + for state: StringName in [ + &"normal", + &"hover", + &"pressed", + &"hover_pressed", + &"focus", + &"disabled", + ]: + button.add_theme_stylebox_override( + state, + _flat_chat_button_style(CHAT_SURFACE_COLOR), + ) func _speech_panel_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() - style.bg_color = Color(UtilityPageStyle.PAPER_ALT, 0.96) - style.border_color = Color(UtilityPageStyle.BORDER, 0.88) - style.set_border_width_all(2) + style.bg_color = Color(UtilityPageStyle.OCEAN_PANEL_MID, 0.96) + style.set_border_width_all(0) style.set_corner_radius_all(12) + style.anti_aliasing = false style.content_margin_left = 10 style.content_margin_right = 10 style.content_margin_top = 6 style.content_margin_bottom = 6 - style.shadow_color = Color(0, 0, 0, 0.32) - style.shadow_size = 4 - style.shadow_offset = Vector2(2, 3) return style +func _create_speech_pointer() -> Polygon2D: + var pointer := Polygon2D.new() + pointer.name = "SpeechPointer" + pointer.polygon = PackedVector2Array([ + Vector2(-SPEECH_POINTER_HALF_WIDTH, 0.0), + Vector2(SPEECH_POINTER_HALF_WIDTH, 0.0), + Vector2(0.0, SPEECH_POINTER_HEIGHT), + ]) + pointer.color = Color(UtilityPageStyle.OCEAN_PANEL_MID, 0.96) + pointer.antialiased = false + pointer.z_index = -1 + return pointer + + +func _on_entry_text_submitted(_value: String) -> void: + _last_submit_frame = Engine.get_process_frames() + _send() + + func _send() -> void: if _send_pending: return var body := _entry.text + if body.strip_edges().is_empty(): + close_chat() + return + if _handle_chat_command(body): + return _send_pending = true _pending_send_body = NetworkChatProtocol.sanitize_body(body) _entry.editable = false @@ -509,6 +766,72 @@ func _send() -> void: _set_status("Sending…") +func _handle_chat_command(body: String) -> bool: + var command_text := body.strip_edges() + if not command_text.begins_with("/"): + return false + var parts: PackedStringArray = command_text.split(" ", false) + var command := String(parts[0]).trim_prefix("/").to_lower() + match command: + "time": + _handle_time_command(parts) + "weather": + _handle_weather_command(parts) + "": + _set_status("Enter a command after /.") + _: + _set_status("Unknown command: /%s" % command) + _entry.clear() + _flush_draft() + return true + + +func _handle_time_command(parts: PackedStringArray) -> void: + if parts.size() != 2: + _set_status("Usage: /time [dawn, day, dusk, night]") + return + if _service == null or _world_time == null: + _set_status("World time is unavailable.") + return + var phase_name := String(parts[1]).to_lower() + if phase_name not in ["dawn", "day", "dusk", "night"]: + _set_status("Usage: /time [dawn, day, dusk, night]") + return + if not _service.request_world_time_change(phase_name): + _set_status("Only the host or an operator can change world time.") + return + close_chat() + + +func _handle_weather_command(parts: PackedStringArray) -> void: + if parts.size() != 2: + _set_status("Usage: /weather [clear, cloudy, rainy, foggy]") + return + if _service == null or _world_weather == null: + _set_status("World weather is unavailable.") + return + var weather_name := String(parts[1]).to_lower() + match weather_name: + "clear", "sunny": + weather_name = "clear" + "cloudy", "rainy", "foggy": + pass + _: + _set_status("Usage: /weather [clear, cloudy, rainy, foggy]") + return + if not _service.request_world_weather_change(weather_name): + _set_status("Only the host or an operator can change world weather.") + return + close_chat() + + +func _on_world_command_finished(success: bool, message: String) -> void: + if success: + _set_status("") + elif not message.is_empty(): + _set_status(message) + + func _on_local_message_confirmed(message: Dictionary) -> void: if ( not _send_pending @@ -534,28 +857,69 @@ func _on_message(message: Dictionary) -> void: if int(message["kind"]) != NetworkChatProtocol.Kind.PLAYER: return var peer_id: int = message["sender_peer_id"] + _show_speech_bubble( + peer_id, + str(message["body"]), + str(message.get("sender_fingerprint", "")), + ) + + +func show_local_speech(body: String) -> void: + if body.strip_edges().is_empty() or _session == null: + return + _show_speech_bubble(_session.get_local_peer_id(), body, "") + + +func _show_speech_bubble( + peer_id: int, + body: String, + fingerprint: String, +) -> void: _on_peer_removed(peer_id) var bubble := PanelContainer.new() bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE - bubble.custom_minimum_size = Vector2(270, 0) - bubble.size = Vector2(270, 72) + bubble.custom_minimum_size = Vector2(SPEECH_BUBBLE_WIDTH, 0.0) bubble.add_theme_stylebox_override("panel", _speech_panel_style()) + var pointer := _create_speech_pointer() + bubble.add_child(pointer) var label := Label.new() - label.text = str(message["body"]).left(120) - label.custom_minimum_size = Vector2(246, 46) - label.size = Vector2(246, 62) + label.text = body + label.custom_minimum_size = Vector2( + SPEECH_BUBBLE_WIDTH - 20.0, + 0.0, + ) label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER label.mouse_filter = Control.MOUSE_FILTER_IGNORE label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) - label.add_theme_color_override("font_color", UtilityPageStyle.INK) + label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) bubble.add_child(label) _speech_layer.add_child(bubble) + var reveal_seconds := TypewriterRevealType.start(label) + var voice_profile_id: String = VoiceProfilesType.DEFAULT_ID + var speaker_avatar := _spawn.get_avatar(peer_id) + if speaker_avatar != null: + voice_profile_id = VoiceProfilesType.sanitized_id( + speaker_avatar.get_animalese_voice_id() + ) + _animalese_voice.speak_text( + label, + label.text, + str(peer_id), + voice_profile_id, + ) _speech[peer_id] = { "bubble": bubble, - "expires": Time.get_ticks_msec() / 1000.0 + SPEECH_SECONDS, - "fingerprint": str(message.get("sender_fingerprint", "")), + "pointer": pointer, + "expires": ( + Time.get_ticks_msec() / 1000.0 + + reveal_seconds + + SPEECH_SECONDS + ), + "fingerprint": fingerprint, } @@ -579,12 +943,13 @@ func _on_rejected(message: String) -> void: _set_status(message) if not _opened: open_chat() + else: + call_deferred("_focus_entry_after_open") func _refresh_history() -> void: if _service == null: return - var scroll_state := _capture_history_scroll() var lines: Array[String] = [] var messages := _service.get_history() for message: Dictionary in messages: @@ -595,7 +960,7 @@ func _refresh_history() -> void: str(message["sender_display_name"]), str(message["body"]), ]) _history.text = "\n".join(lines) - _restore_history_scroll.call_deferred(scroll_state) + _scroll_history_to_bottom.call_deferred() func _refresh_visibility() -> void: @@ -603,6 +968,17 @@ func _refresh_visibility() -> void: _panel.hide() _clock_panel.hide() _weather_icon.hide() + _fish_finder_effect_icon.hide() + _collapse_button.hide() + _height_button.hide() + _unread_indicator.hide() + _hint.hide() + return + if _hud_hidden and not _opened: + _panel.hide() + _clock_panel.hide() + _weather_icon.hide() + _fish_finder_effect_icon.hide() _collapse_button.hide() _height_button.hide() _unread_indicator.hide() @@ -610,10 +986,22 @@ func _refresh_visibility() -> void: return _clock_panel.show() _weather_icon.show() + _fish_finder_effect_icon.visible = ( + _item_effects != null + and _item_effects.is_active(PlayerItemEffects.FISH_FINDER_ID) + ) _collapse_button.show() var collapsed := _presentation_state == PresentationState.COLLAPSED _panel.show() - _height_button.show() + _collapse_button.focus_mode = ( + Control.FOCUS_ALL if _opened else Control.FOCUS_NONE + ) + _height_button.visible = not _mobile_mode + _height_button.focus_mode = ( + Control.FOCUS_ALL + if _opened and not _mobile_mode + else Control.FOCUS_NONE + ) _unread_indicator.visible = collapsed and _collapsed_has_unread _hint.visible = not _opened @@ -655,6 +1043,8 @@ func set_dock_right(should_dock_right: bool) -> void: if not is_node_ready() or _panel == null: return _panel.add_theme_stylebox_override("panel", _chat_panel_style()) + _apply_flat_chat_button(_collapse_button) + _apply_flat_chat_button(_height_button) _refresh_handle_labels(_presentation_state) _layout_presentation(false) @@ -663,22 +1053,55 @@ func is_docked_right() -> bool: return _dock_right +func set_mobile_mode(enabled: bool) -> void: + _mobile_mode = enabled + if not is_node_ready() or _panel == null: + return + if _mobile_mode: + if _presentation_state == PresentationState.EXPANDED: + _presentation_state = PresentationState.COMPACT + if _visible_state_before_collapse == PresentationState.EXPANDED: + _visible_state_before_collapse = PresentationState.COMPACT + _panel.add_theme_stylebox_override("panel", _chat_panel_style()) + _refresh_handle_labels(_presentation_state) + _refresh_visibility() + _layout_presentation(false) + + +func is_mobile_mode() -> bool: + return _mobile_mode + + +func set_hud_hidden(hidden: bool) -> void: + _hud_hidden = hidden + _refresh_visibility() + + +func is_hud_hidden() -> bool: + return _hud_hidden + + func _refresh_handle_labels(state: PresentationState) -> void: var collapsed := state == PresentationState.COLLAPSED var height_state := _visible_state_before_collapse if collapsed else state - if _dock_right: - _collapse_button.text = "<" if collapsed else ">" - else: - _collapse_button.text = ">" if collapsed else "<" + _collapse_button.text = "" + _collapse_button.icon = CHAT_SHOW_ICON if collapsed else CHAT_HIDE_ICON _collapse_button.tooltip_text = ( "Show chat" if collapsed else "Hide chat" ) - _height_button.text = "v" if height_state == PresentationState.EXPANDED else "^" + _collapse_button.accessibility_name = _collapse_button.tooltip_text + _height_button.text = "" + _height_button.icon = ( + CHAT_COMPACT_ICON + if height_state == PresentationState.EXPANDED + else CHAT_EXPAND_ICON + ) _height_button.tooltip_text = ( "Compact chat" if height_state == PresentationState.EXPANDED else "Expand chat history" ) + _height_button.accessibility_name = _height_button.tooltip_text func _update_panel_opacity(immediate_recheck: bool = false) -> void: @@ -687,10 +1110,8 @@ func _update_panel_opacity(immediate_recheck: bool = false) -> void: if _presentation_state == PresentationState.COLLAPSED: if _height_tween == null or not _height_tween.is_running(): _panel.modulate.a = IDLE_ALPHA - _collapse_button.modulate.a = ( - 1.0 if _collapsed_has_unread else 0.82 - ) - _height_button.modulate.a = 0.82 + _collapse_button.modulate.a = IDLE_ALPHA + _height_button.modulate.a = IDLE_ALPHA return var recent := ( Time.get_ticks_msec() / 1000.0 - _last_message_time < RECENT_SECONDS @@ -735,6 +1156,9 @@ func _update_speech() -> void: if camera.is_position_behind(world_position): bubble.hide() continue + if _is_speech_world_occluded(camera, avatar, world_position): + bubble.hide() + continue var screen_position := ( camera.unproject_position(world_position) / _output_scale ) @@ -748,15 +1172,65 @@ func _update_speech() -> void: bubble.hide() continue var desired := screen_position - Vector2( - bubble.size.x * 0.5, bubble.size.y + 8.0 + bubble.size.x * 0.5, + bubble.size.y + SPEECH_POINTER_HEIGHT, ) bubble.position = Vector2( clampf(desired.x, 8.0, viewport_size.x - bubble.size.x - 8.0), - clampf(desired.y, 8.0, viewport_size.y - bubble.size.y - 8.0), + clampf( + desired.y, + 8.0, + viewport_size.y + - bubble.size.y + - SPEECH_POINTER_HEIGHT + - 8.0, + ), ) + var pointer := state.get("pointer") as Polygon2D + if pointer != null: + pointer.position = Vector2( + clampf( + screen_position.x - bubble.position.x, + SPEECH_POINTER_HALF_WIDTH + 12.0, + bubble.size.x - SPEECH_POINTER_HALF_WIDTH - 12.0, + ), + bubble.size.y - SPEECH_POINTER_OVERLAP, + ) bubble.show() +func _is_speech_world_occluded( + camera: Camera3D, + speaker: Player, + world_position: Vector3, +) -> bool: + var world := camera.get_world_3d() + if world == null: + return false + var excluded: Array[RID] = [] + if _player != null: + excluded.append(_player.get_rid()) + if speaker != _player: + excluded.append(speaker.get_rid()) + var query := PhysicsRayQueryParameters3D.create( + camera.global_position, + world_position, + ) + query.collide_with_areas = false + query.collide_with_bodies = true + for _attempt: int in range(8): + query.exclude = excluded + var hit: Dictionary = world.direct_space_state.intersect_ray(query) + if hit.is_empty(): + return false + var collider := hit.get("collider") as CollisionObject3D + if collider is Player: + excluded.append(collider.get_rid()) + continue + return true + return false + + func _on_draft_changed(_value: String) -> void: if _settings == null: return @@ -791,32 +1265,98 @@ func _layout_presentation(animate: bool) -> void: var viewport_height := size.y if viewport_height <= 0.0: viewport_height = 720.0 + var layout_state := ( + _visible_state_before_collapse + if _presentation_state == PresentationState.COLLAPSED + else _presentation_state + ) + var collapsed := _presentation_state == PresentationState.COLLAPSED var bottom_margin := minf( BOTTOM_MARGIN, maxf(MIN_TOP_MARGIN, (viewport_height - MIN_HISTORY_HEIGHT) * 0.25), ) var bottom := viewport_height - bottom_margin var height := COMPACT_HEIGHT - var layout_state := ( - _visible_state_before_collapse - if _presentation_state == PresentationState.COLLAPSED - else _presentation_state - ) - if layout_state == PresentationState.EXPANDED: - height = maxf(MIN_HISTORY_HEIGHT, viewport_height - 2.0 * bottom_margin) - height = minf(height, maxf(MIN_HISTORY_HEIGHT, bottom - MIN_TOP_MARGIN)) - var collapsed := _presentation_state == PresentationState.COLLAPSED - var panel_x: float - if _dock_right: - panel_x = ( - viewport_width - COLLAPSED_REVEAL_WIDTH - if collapsed - else viewport_width - PANEL_WIDTH + var panel_width := PANEL_WIDTH + var panel_x: float = 0.0 + var panel_y: float = 0.0 + var collapse_position := Vector2.ZERO + var height_position := Vector2.ZERO + var unread_position := Vector2.ZERO + if _mobile_mode: + panel_width = ( + MOBILE_EXPANDED_WIDTH + if layout_state == PresentationState.EXPANDED + else MOBILE_COMPACT_WIDTH ) + panel_width = minf( + panel_width, + maxf(1.0, viewport_width - MOBILE_EDGE_MARGIN * 2.0), + ) + height = ( + MOBILE_EXPANDED_HEIGHT + if layout_state == PresentationState.EXPANDED + else MOBILE_COMPACT_HEIGHT + ) + height = minf( + height, + maxf(MIN_HISTORY_HEIGHT, viewport_height - HANDLE_SIZE.y), + ) + panel_x = (viewport_width - panel_width) * 0.5 + panel_y = -(height - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0 + var mobile_handle_y: float = ( + COLLAPSED_REVEAL_WIDTH if collapsed else height + ) + collapse_position = Vector2( + panel_x + panel_width - HANDLE_SIZE.x, + mobile_handle_y, + ) + height_position = Vector2( + collapse_position.x - HANDLE_SIZE.x - HANDLE_GAP, + mobile_handle_y, + ) + unread_position = collapse_position + Vector2(6.0, -18.0) else: - panel_x = -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0 - var target_position := Vector2(panel_x, bottom - height) - var target_size := Vector2(PANEL_WIDTH, height) + if layout_state == PresentationState.EXPANDED: + height = maxf( + MIN_HISTORY_HEIGHT, + viewport_height - bottom_margin - EXPANDED_TOP_MARGIN, + ) + height = minf( + height, + maxf(MIN_HISTORY_HEIGHT, bottom - MIN_TOP_MARGIN), + ) + if _dock_right: + panel_x = ( + viewport_width - COLLAPSED_REVEAL_WIDTH + if collapsed + else viewport_width - PANEL_WIDTH + ) + else: + panel_x = ( + -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) + if collapsed + else 0.0 + ) + panel_y = bottom - height + var handle_stack_top := panel_y + HANDLE_TOP_INSET + var handle_x: float = ( + panel_x - HANDLE_SIZE.x + if _dock_right + else COLLAPSED_REVEAL_WIDTH if collapsed else PANEL_WIDTH + ) + collapse_position = Vector2( + handle_x, + handle_stack_top + HANDLE_SIZE.y + HANDLE_GAP, + ) + height_position = Vector2(handle_x, handle_stack_top) + var unread_offset_x := -18.0 if _dock_right else 6.0 + unread_position = collapse_position + Vector2( + unread_offset_x, + -18.0, + ) + var target_position := Vector2(panel_x, panel_y) + var target_size := Vector2(panel_width, height) var clock_x: float = ( viewport_width - CLOCK_SIZE.x - CLOCK_EDGE_MARGIN if _dock_right else CLOCK_EDGE_MARGIN @@ -825,6 +1365,11 @@ func _layout_presentation(animate: bool) -> void: clock_x, CLOCK_EDGE_MARGIN, ) + var fish_finder_effect_position := Vector2( + clock_position.x + + (CLOCK_SIZE.x - FISH_FINDER_EFFECT_ICON_SIZE.x) * 0.5, + clock_position.y + CLOCK_SIZE.y + 2.0, + ) var weather_icon_x: float = ( clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x if _dock_right @@ -833,22 +1378,6 @@ func _layout_presentation(animate: bool) -> void: var weather_icon_position := Vector2( weather_icon_x, clock_position.y ) - var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP - var handle_stack_top := ( - bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5 - ) - var handle_x: float - if _dock_right: - handle_x = panel_x - HANDLE_SIZE.x - else: - handle_x = COLLAPSED_REVEAL_WIDTH if collapsed else PANEL_WIDTH - var collapse_position := Vector2( - handle_x, - handle_stack_top + HANDLE_SIZE.y + HANDLE_GAP, - ) - var height_position := Vector2(handle_x, handle_stack_top) - var unread_offset_x := -18.0 if _dock_right else 6.0 - var unread_position := collapse_position + Vector2(unread_offset_x, -18.0) var hint_position := Vector2( ( viewport_width - _hint.size.x - HINT_EDGE_MARGIN @@ -864,6 +1393,8 @@ func _layout_presentation(animate: bool) -> void: _panel.size = target_size _clock_panel.position = clock_position _clock_panel.size = CLOCK_SIZE + _fish_finder_effect_icon.position = fish_finder_effect_position + _fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE _weather_icon.position = weather_icon_position _weather_icon.size = WEATHER_ICON_SIZE _collapse_button.position = collapse_position @@ -871,7 +1402,6 @@ func _layout_presentation(animate: bool) -> void: _unread_indicator.position = unread_position _hint.position = hint_position return - var scroll_state := _capture_history_scroll() _height_tween = create_tween().set_parallel(true) _height_tween.tween_property( _panel, "position", target_position, UIMotion.CHAT_RESIZE_DURATION @@ -885,6 +1415,12 @@ func _layout_presentation(animate: bool) -> void: clock_position, UIMotion.CHAT_RESIZE_DURATION, ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + _height_tween.tween_property( + _fish_finder_effect_icon, + "position", + fish_finder_effect_position, + UIMotion.CHAT_RESIZE_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _height_tween.tween_property( _weather_icon, "position", @@ -910,38 +1446,18 @@ func _layout_presentation(animate: bool) -> void: UIMotion.CHAT_RESIZE_DURATION, ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _height_tween.finished.connect(func() -> void: - _restore_history_scroll(scroll_state) + _scroll_history_to_bottom() ) _hint.position = hint_position -func _capture_history_scroll() -> Dictionary: - if _history == null: - return {"pinned": true, "value": 0.0} - var scroll := _history.get_v_scroll_bar() - if scroll == null: - return {"pinned": true, "value": 0.0} - var bottom := maxf(scroll.min_value, scroll.max_value - scroll.page) - return { - "pinned": scroll.value >= bottom - 2.0, - "value": scroll.value, - } - - -func _restore_history_scroll(state: Dictionary) -> void: +func _scroll_history_to_bottom() -> void: if _history == null: return var scroll := _history.get_v_scroll_bar() if scroll == null: return - if bool(state.get("pinned", true)): - scroll.value = maxf(scroll.min_value, scroll.max_value - scroll.page) - else: - scroll.value = clampf( - float(state.get("value", 0.0)), - scroll.min_value, - maxf(scroll.min_value, scroll.max_value - scroll.page), - ) + scroll.value = maxf(scroll.min_value, scroll.max_value - scroll.page) func _on_viewport_resized() -> void: diff --git a/ui/components/bubble_hotbar/bubble_hotbar_slot.gd b/ui/components/bubble_hotbar/bubble_hotbar_slot.gd index 11a7db3..4bebdfe 100644 --- a/ui/components/bubble_hotbar/bubble_hotbar_slot.gd +++ b/ui/components/bubble_hotbar/bubble_hotbar_slot.gd @@ -56,6 +56,8 @@ var _base_position: Vector2 = Vector2.ZERO var _presented_size: Vector2 = Vector2.ZERO var _compact: bool = false var _presentation_initialized: bool = false +var _controller_preview_active: bool = false +var _controller_preview_texture: Texture2D func _ready() -> void: @@ -146,6 +148,15 @@ func set_drag_enabled(enabled: bool) -> void: _hovered = false +func set_controller_placement_preview( + active: bool, + texture: Texture2D, +) -> void: + _controller_preview_active = active + _controller_preview_texture = texture + refresh() + + func refresh() -> void: if _hotbar == null: return @@ -161,11 +172,16 @@ func refresh() -> void: if _fish_inventory != null and not catch_id.is_empty() else null ) - _item_icon.texture = ( + var assigned_texture: Texture2D = ( fish_catch.fish.display_texture if fish_catch != null else item.icon if item != null else null ) + _item_icon.texture = ( + _controller_preview_texture + if _controller_preview_active + else assigned_texture + ) var quantity: int = ( _bag.get_quantity(item_id) if _bag != null and not item_id.is_empty() @@ -177,7 +193,9 @@ func refresh() -> void: else "" ) _quantity_label.text = quantity_text - _quantity_label.visible = not quantity_text.is_empty() + _quantity_label.visible = ( + not _controller_preview_active and not quantity_text.is_empty() + ) tooltip_text = ( "%s · %.1f lb" % [ FishQualityType.qualified_name( @@ -192,7 +210,11 @@ func refresh() -> void: var was_selected: bool = _selected var was_empty: bool = _empty _selected = slot_index == _hotbar.get_selected_slot() - _empty = item == null and fish_catch == null + _empty = ( + not _controller_preview_active + and item == null + and fish_catch == null + ) if was_selected != _selected or was_empty != _empty: _apply_style() @@ -220,7 +242,9 @@ func _apply_style() -> void: var normal_fill: Color = profile.normal_fill normal_fill.a = 1.0 var selected_fill: Color = normal_fill - if _selected: + if _controller_preview_active: + selected_fill = normal_fill.lightened(0.22) + elif _selected: selected_fill = normal_fill.lightened(0.12) add_theme_stylebox_override( "normal", diff --git a/ui/components/bubble_menu/README.md b/ui/components/bubble_menu/README.md deleted file mode 100644 index ca549bb..0000000 --- a/ui/components/bubble_menu/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Bubble menu authoring - -Instance `bubble_button.tscn` for each action, or attach `bubble_button.gd` -to an existing Button. Set its neutral size, desktop and compact anchors, -font-size limits, and deterministic motion values in the Inspector. A label -child can be assigned through `label_control_path` when wrapped text is needed. -Text size is calculated once per layout from the neutral smaller dimension and -the profile ratio. - -Place the buttons under a Control using `bubble_cluster.gd`. Give the cluster -the ordered BubbleButton references with `configure()`, then call -`apply_layout()` when its available size or responsive layout changes. The -button order defines explicit keyboard and controller focus neighbors. Compact -anchors and minimum sizes remain authored per button. - -The shared profile owns the palette, rounded styles, proportional-font ratio, -hover response, and contact tuning. Labels, actions, sizes, anchors, per-button -motion, responsive wrapping, availability, and confirmation behavior remain -owned by the menu. Connect each Button's `pressed` signal in that parent menu. - -`motion_scale` defaults to `1.0`. Setting it to `0.0` removes idle drift and -deformation while retaining hover and focus feedback. - -Contact is a deterministic, bounded visual correction around authored anchors. -Real physics is intentionally avoided so layouts, focus order, and hit targets -remain stable. diff --git a/ui/components/bubble_menu/bubble_button.gd b/ui/components/bubble_menu/bubble_button.gd index 15acb0e..f01d50b 100644 --- a/ui/components/bubble_menu/bubble_button.gd +++ b/ui/components/bubble_menu/bubble_button.gd @@ -1,6 +1,8 @@ class_name BubbleButton extends Button +const ICON_FILL_RATIO: float = 0.76 + @export var profile: BubbleMenuProfile @export_group("Authored Layout") @@ -37,6 +39,54 @@ func _ready() -> void: resized.connect(_update_pivot) _update_pivot() apply_profile() + _apply_icon_presentation(neutral_size) + + +func _gui_input(event: InputEvent) -> void: + if _adjustment_direction(self) == 0: + return + var direction: int = 0 + if event.is_action_pressed(&"ui_left"): + direction = -1 + elif event.is_action_pressed(&"ui_right"): + direction = 1 + if direction == 0: + return + var adjustment_button: BaseButton = _find_adjustment_button(direction) + if adjustment_button == null: + return + adjustment_button.pressed.emit() + accept_event() + + +func _find_adjustment_button(direction: int) -> BaseButton: + var parent_node: Node = get_parent() + if parent_node == null: + return null + for child: Node in parent_node.get_children(): + var button := child as BaseButton + if ( + button != null + and button.visible + and not button.disabled + and _adjustment_direction(button) == direction + ): + return button + return null + + +func _adjustment_direction(button: BaseButton) -> int: + var descriptions: Array[String] = [ + button.text.strip_edges().to_lower(), + button.tooltip_text.strip_edges().to_lower(), + button.accessibility_name.strip_edges().to_lower(), + ] + for description: String in descriptions: + if description in ["-", "−", "minus", "decrease"]: + return -1 + if description in ["+", "plus", "increase"]: + return 1 + return 0 func apply_layout( @@ -49,6 +99,7 @@ func apply_layout( neutral_position = position presented_size = bubble_size _update_pivot() + _apply_icon_presentation(bubble_size) var label_control: Control = get_label_control() var font_size := clampi( roundi(minf(bubble_size.x, bubble_size.y) * font_size_ratio), @@ -63,6 +114,9 @@ func get_layout_size(layout_scale: float, compact: bool) -> Vector2: if compact: bubble_size.x = maxf(bubble_size.x, compact_minimum_size.x) bubble_size.y = maxf(bubble_size.y, compact_minimum_size.y) + if _uses_icon_only_presentation(): + var diameter: float = maxf(bubble_size.x, bubble_size.y) + bubble_size = Vector2(diameter, diameter) return bubble_size @@ -78,6 +132,24 @@ func get_label_control() -> Control: return self +func _uses_icon_only_presentation() -> bool: + return icon != null and text.is_empty() and label_control_path.is_empty() + + +func _apply_icon_presentation(bubble_size: Vector2) -> void: + if not _uses_icon_only_presentation(): + return + alignment = HORIZONTAL_ALIGNMENT_CENTER + icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + expand_icon = true + texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + add_theme_constant_override( + "icon_max_width", + maxi(1, roundi(minf(bubble_size.x, bubble_size.y) * ICON_FILL_RATIO)), + ) + + func advance_emphasis(delta: float) -> void: var target: float = 1.0 if _hovered or _focused else 0.0 emphasis = move_toward( @@ -122,6 +194,8 @@ func calculate_visual_scale( * motion_scale * lerpf(1.0, profile.emphasized_deformation_scale, emphasis) ) + if _uses_icon_only_presentation(): + deformation_amount = 0.0 var hover_scale: float = lerpf( 1.0, profile.hover_focus_scale, diff --git a/ui/components/bubble_menu/bubble_cluster.gd b/ui/components/bubble_menu/bubble_cluster.gd index 9a8d4d0..0ad2624 100644 --- a/ui/components/bubble_menu/bubble_cluster.gd +++ b/ui/components/bubble_menu/bubble_cluster.gd @@ -1,6 +1,10 @@ class_name BubbleCluster extends Control +const ControllerFocusNavigationType = preload( + "res://ui/controller_focus_navigation.gd" +) + @export var profile: BubbleMenuProfile @export var desktop_reference_size: Vector2 = Vector2(396.0, 318.0) @export var compact_reference_size: Vector2 = Vector2(294.0, 200.0) @@ -18,14 +22,6 @@ func configure(bubbles: Array[BubbleButton]) -> void: if bubble.profile == null: bubble.profile = profile bubble.apply_profile() - if index > 0: - bubble.focus_neighbor_top = bubble.get_path_to( - _bubbles[index - 1] - ) - if index + 1 < _bubbles.size(): - bubble.focus_neighbor_bottom = bubble.get_path_to( - _bubbles[index + 1] - ) func apply_layout(field_size: Vector2, compact: bool) -> void: @@ -52,6 +48,7 @@ func apply_layout(field_size: Vector2, compact: bool) -> void: bubble_size, profile.font_size_ratio ) + ControllerFocusNavigationType.configure_spatial_neighbors(_bubbles) func advance_motion(delta: float) -> void: diff --git a/ui/components/bubble_menu/bubble_status_bubble.gd b/ui/components/bubble_menu/bubble_status_bubble.gd index 2a475fc..dc43d5c 100644 --- a/ui/components/bubble_menu/bubble_status_bubble.gd +++ b/ui/components/bubble_menu/bubble_status_bubble.gd @@ -5,6 +5,7 @@ extends PanelContainer @onready var _heading: Label = %Heading @onready var _value: Label = %Value +@onready var _currency_icon: TextureRect = %CurrencyIcon func _ready() -> void: @@ -16,3 +17,10 @@ func _ready() -> void: func set_content(heading: String, value: String) -> void: _heading.text = heading _value.text = value + _currency_icon.visible = false + + +func set_currency_amount(heading: String, amount: int) -> void: + _heading.text = heading + _value.text = str(amount) + _currency_icon.visible = true diff --git a/ui/components/bubble_menu/bubble_status_bubble.tscn b/ui/components/bubble_menu/bubble_status_bubble.tscn index eb5bd9b..b898418 100644 --- a/ui/components/bubble_menu/bubble_status_bubble.tscn +++ b/ui/components/bubble_menu/bubble_status_bubble.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=3 format=3] +[gd_scene load_steps=4 format=3] [ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_status_bubble.gd" id="1_status"] [ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"] +[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="3_currency"] [node name="BubbleStatusBubble" type="PanelContainer"] custom_minimum_size = Vector2(112, 62) @@ -23,7 +24,25 @@ theme_override_font_sizes/font_size = 13 text = "status" horizontal_alignment = 1 -[node name="Value" type="Label" parent="Labels"] +[node name="ValueRow" type="HBoxContainer" parent="Labels"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 3 +alignment = 1 + +[node name="CurrencyIcon" type="TextureRect" parent="Labels/ValueRow"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(18, 18) +layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 2 +texture = ExtResource("3_currency") +expand_mode = 1 +stretch_mode = 5 +texture_filter = 1 + +[node name="Value" type="Label" parent="Labels/ValueRow"] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 diff --git a/ui/components/bubble_menu/cooler_fish_sprite.gd b/ui/components/bubble_menu/cooler_fish_sprite.gd index d491969..be47c17 100644 --- a/ui/components/bubble_menu/cooler_fish_sprite.gd +++ b/ui/components/bubble_menu/cooler_fish_sprite.gd @@ -19,6 +19,7 @@ var _quality_color := Color.WHITE func _ready() -> void: + set_meta(&"controller_focus_inversion_disabled", true) resized.connect(_update_visual_pivot) focus_entered.connect(_refresh_style) focus_exited.connect(_refresh_style) diff --git a/ui/components/currency_amount.gd b/ui/components/currency_amount.gd new file mode 100644 index 0000000..bda2f7e --- /dev/null +++ b/ui/components/currency_amount.gd @@ -0,0 +1,48 @@ +class_name CurrencyAmount +extends HBoxContainer + +@export var amount: int = 0: + set(value): + amount = value + _display_text = str(value) + _refresh_amount() +@export_range(8.0, 64.0, 1.0) var icon_size: float = 18.0: + set(value): + icon_size = value + _refresh_icon_size() + +@onready var _icon: TextureRect = %Icon +@onready var _amount_label: Label = %Amount + +var _display_text: String = "0" + + +func _ready() -> void: + mouse_filter = Control.MOUSE_FILTER_IGNORE + _refresh_icon_size() + _refresh_amount() + + +func set_amount(value: int) -> void: + amount = value + + +func set_amount_text(value: String) -> void: + _display_text = value + _refresh_amount() + + +func get_amount_label() -> Label: + if _amount_label != null: + return _amount_label + return get_node_or_null("Amount") as Label + + +func _refresh_amount() -> void: + if _amount_label != null: + _amount_label.text = _display_text + + +func _refresh_icon_size() -> void: + if _icon != null: + _icon.custom_minimum_size = Vector2.ONE * icon_size diff --git a/ui/components/currency_amount.gd.uid b/ui/components/currency_amount.gd.uid new file mode 100644 index 0000000..dfb2290 --- /dev/null +++ b/ui/components/currency_amount.gd.uid @@ -0,0 +1 @@ +uid://b7jx040ujcol diff --git a/ui/components/currency_amount.tscn b/ui/components/currency_amount.tscn new file mode 100644 index 0000000..00c8ace --- /dev/null +++ b/ui/components/currency_amount.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/components/currency_amount.gd" id="1_script"] +[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="2_coin"] + +[node name="CurrencyAmount" type="HBoxContainer"] +mouse_filter = 2 +theme_override_constants/separation = 3 +alignment = 1 +script = ExtResource("1_script") + +[node name="Icon" type="TextureRect" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(18, 18) +layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 2 +texture = ExtResource("2_coin") +expand_mode = 1 +stretch_mode = 5 +texture_filter = 1 + +[node name="Amount" type="Label" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 2 +text = "0" +vertical_alignment = 1 diff --git a/ui/components/organizer_tab.gd b/ui/components/organizer_tab.gd index 8db5122..3625b9a 100644 --- a/ui/components/organizer_tab.gd +++ b/ui/components/organizer_tab.gd @@ -93,14 +93,14 @@ func _initialize_motion() -> void: _set_visual_y(_target_y()) -func _on_toggled(pressed: bool) -> void: - if _selected and not pressed: +func _on_toggled(is_pressed: bool) -> void: + if _selected and not is_pressed: # A selected organizer tab is a page marker, not a collapsible toggle. # Restore without another signal before any lower-state frame is drawn. set_pressed_no_signal(true) refresh_state(false) return - _selected = pressed + _selected = is_pressed refresh_state() diff --git a/ui/controller_focus_navigation.gd b/ui/controller_focus_navigation.gd new file mode 100644 index 0000000..cd93796 --- /dev/null +++ b/ui/controller_focus_navigation.gd @@ -0,0 +1,57 @@ +class_name ControllerFocusNavigation +extends RefCounted + +const PERPENDICULAR_WEIGHT: float = 2.5 +const MINIMUM_FORWARD_DISTANCE: float = 0.5 + + +static func configure_spatial_neighbors(controls: Array) -> void: + var candidates: Array[Control] = [] + for item: Variant in controls: + var control := item as Control + if control == null or not control.is_visible_in_tree(): + continue + candidates.append(control) + for control: Control in candidates: + control.focus_neighbor_left = _neighbor_path( + control, candidates, Vector2.LEFT + ) + control.focus_neighbor_right = _neighbor_path( + control, candidates, Vector2.RIGHT + ) + control.focus_neighbor_top = _neighbor_path( + control, candidates, Vector2.UP + ) + control.focus_neighbor_bottom = _neighbor_path( + control, candidates, Vector2.DOWN + ) + + +static func _neighbor_path( + origin: Control, + candidates: Array[Control], + direction: Vector2, +) -> NodePath: + var origin_center: Vector2 = origin.get_global_rect().get_center() + var best: Control = null + var best_score: float = INF + for candidate: Control in candidates: + if candidate == origin or candidate.focus_mode == Control.FOCUS_NONE: + continue + var delta: Vector2 = ( + candidate.get_global_rect().get_center() - origin_center + ) + var forward_distance: float = delta.dot(direction) + if forward_distance <= MINIMUM_FORWARD_DISTANCE: + continue + var perpendicular_distance: float = absf(delta.cross(direction)) + var score: float = ( + forward_distance + + perpendicular_distance * PERPENDICULAR_WEIGHT + ) + if score < best_score: + best = candidate + best_score = score + if best == null: + return NodePath() + return origin.get_path_to(best) diff --git a/ui/controller_focus_navigation.gd.uid b/ui/controller_focus_navigation.gd.uid new file mode 100644 index 0000000..d13ed12 --- /dev/null +++ b/ui/controller_focus_navigation.gd.uid @@ -0,0 +1 @@ +uid://dfb6bobetmujn diff --git a/ui/controller_focus_presentation.gd b/ui/controller_focus_presentation.gd new file mode 100644 index 0000000..7223ca7 --- /dev/null +++ b/ui/controller_focus_presentation.gd @@ -0,0 +1,99 @@ +class_name ControllerFocusPresentation +extends Node + +const CONTROLLER_MOTION_THRESHOLD: float = 0.35 +const INVERSION_DISABLED_META: StringName = &"controller_focus_inversion_disabled" + +var _controller_active: bool = false +var _focused_item: CanvasItem +var _original_material: Material +var _inversion_material: ShaderMaterial + + +func _ready() -> void: + var inversion_shader := Shader.new() + inversion_shader.code = """ +shader_type canvas_item; +render_mode unshaded; + +void fragment() { + vec4 source = texture(TEXTURE, UV) * COLOR; + COLOR = vec4(vec3(1.0) - source.rgb, source.a); +} +""" + _inversion_material = ShaderMaterial.new() + _inversion_material.shader = inversion_shader + get_viewport().gui_focus_changed.connect(_on_focus_changed) + set_process_input(true) + set_process(true) + + +func _exit_tree() -> void: + _restore_focused_item() + + +func _input(event: InputEvent) -> void: + if event is InputEventJoypadButton: + if (event as InputEventJoypadButton).pressed: + _set_controller_active(true) + elif event is InputEventJoypadMotion: + if absf((event as InputEventJoypadMotion).axis_value) >= ( + CONTROLLER_MOTION_THRESHOLD + ): + _set_controller_active(true) + elif event is InputEventMouseMotion: + _set_controller_active(false) + elif event is InputEventMouseButton: + if (event as InputEventMouseButton).pressed: + _set_controller_active(false) + elif event is InputEventKey: + if (event as InputEventKey).pressed: + _set_controller_active(false) + + +func _process(_delta: float) -> void: + if not is_instance_valid(_focused_item): + _focused_item = null + _original_material = null + return + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if ( + focus_owner != _focused_item + or not focus_owner.is_visible_in_tree() + or focus_owner.focus_mode == Control.FOCUS_NONE + or bool(focus_owner.get_meta(INVERSION_DISABLED_META, false)) + ): + _apply_to_focus(focus_owner) + + +func _set_controller_active(active: bool) -> void: + if _controller_active == active: + return + _controller_active = active + _apply_to_focus(get_viewport().gui_get_focus_owner()) + + +func _on_focus_changed(control: Control) -> void: + _apply_to_focus(control) + + +func _apply_to_focus(control: Control) -> void: + _restore_focused_item() + if ( + not _controller_active + or control == null + or not control.is_visible_in_tree() + or control.focus_mode == Control.FOCUS_NONE + or bool(control.get_meta(INVERSION_DISABLED_META, false)) + ): + return + _focused_item = control + _original_material = _focused_item.material + _focused_item.material = _inversion_material + + +func _restore_focused_item() -> void: + if is_instance_valid(_focused_item): + _focused_item.material = _original_material + _focused_item = null + _original_material = null diff --git a/ui/controller_focus_presentation.gd.uid b/ui/controller_focus_presentation.gd.uid new file mode 100644 index 0000000..29b0a72 --- /dev/null +++ b/ui/controller_focus_presentation.gd.uid @@ -0,0 +1 @@ +uid://b2ccvcx1rfd1x diff --git a/ui/controller_focus_recovery.gd b/ui/controller_focus_recovery.gd new file mode 100644 index 0000000..bcaf6c8 --- /dev/null +++ b/ui/controller_focus_recovery.gd @@ -0,0 +1,277 @@ +class_name ControllerFocusRecovery +extends Node + +const CONTROLLER_AXIS_THRESHOLD: float = 0.35 +const SEMANTIC_MATCH_BONUS: float = 1000000.0 +const KEYBOARD_NAVIGATION_ACTIONS: Array[StringName] = [ + &"ui_accept", + &"ui_cancel", + &"ui_up", + &"ui_down", + &"ui_left", + &"ui_right", + &"ui_focus_next", + &"ui_focus_prev", + &"ui_page_up", + &"ui_page_down", + &"ui_home", + &"ui_end", +] + +var _controller_active: bool = false +var _focus_navigation_active: bool = false +var _last_focus_center: Vector2 = Vector2.ZERO +var _last_focus_key: String = "" +var _scope_chain: Array[WeakRef] = [] +var _pending_focus: WeakRef +var _pointer_button_down: bool = false +var _recovery_generation: int = 0 + + +func _ready() -> void: + set_process_input(true) + set_process(true) + get_viewport().gui_focus_changed.connect(_on_gui_focus_changed) + + +func _exit_tree() -> void: + var viewport := get_viewport() + if viewport.gui_focus_changed.is_connected(_on_gui_focus_changed): + viewport.gui_focus_changed.disconnect(_on_gui_focus_changed) + + +func _input(event: InputEvent) -> void: + if event is InputEventJoypadButton: + var button_event := event as InputEventJoypadButton + if button_event.pressed: + _pointer_button_down = false + _controller_active = true + _focus_navigation_active = true + if button_event.button_index == JOY_BUTTON_LEFT_SHOULDER: + _recovery_generation += 1 + _scope_chain.clear() + _pending_focus = null + return + _request_pending_focus() + return + if event is InputEventJoypadMotion: + if absf((event as InputEventJoypadMotion).axis_value) >= ( + CONTROLLER_AXIS_THRESHOLD + ): + _pointer_button_down = false + _controller_active = true + _focus_navigation_active = true + _request_pending_focus() + return + if event is InputEventMouseMotion: + _pointer_button_down = ( + (event as InputEventMouseMotion).button_mask != 0 + ) + _leave_focus_navigation(not _pointer_button_down) + return + if event is InputEventMouseButton: + var mouse_button := event as InputEventMouseButton + if mouse_button.button_index in [ + MOUSE_BUTTON_LEFT, + MOUSE_BUTTON_RIGHT, + MOUSE_BUTTON_MIDDLE, + ]: + _pointer_button_down = mouse_button.pressed + _leave_focus_navigation(false) + if not mouse_button.pressed: + _release_current_pointer_focus.call_deferred() + return + if event is InputEventKey and (event as InputEventKey).pressed: + _pointer_button_down = false + _controller_active = false + if _is_keyboard_navigation_event(event): + _focus_navigation_active = true + _request_pending_focus() + else: + _leave_focus_navigation() + + +func _process(_delta: float) -> void: + if ( + _controller_active + and not _scope_chain.is_empty() + and get_viewport().gui_get_focus_owner() == null + ): + _recover_focus(_recovery_generation) + + +func _on_gui_focus_changed(control: Control) -> void: + _recovery_generation += 1 + if control != null: + if _focus_navigation_active: + _pending_focus = null + if _controller_active and control is BaseButton: + _remember_focus(control) + return + if _keeps_pointer_focus(control): + return + if _is_focusable(control): + _pending_focus = weakref(control) + _release_focus_if_inactive.call_deferred(control) + return + if not _controller_active or _scope_chain.is_empty(): + return + var generation: int = _recovery_generation + _recover_focus.call_deferred(generation) + + +func _remember_focus(control: Control) -> void: + _last_focus_center = control.get_global_rect().get_center() + _last_focus_key = _semantic_key(control) + _scope_chain.clear() + var recovery_root: Node = get_parent() + var ancestor: Node = control.get_parent() + while ancestor != null: + if ancestor is Control: + _scope_chain.append(weakref(ancestor)) + if ancestor == recovery_root: + break + ancestor = ancestor.get_parent() + + +func _recover_focus(generation: int) -> void: + if generation != _recovery_generation: + return + if not _controller_active or get_viewport().gui_get_focus_owner() != null: + return + for scope_reference: WeakRef in _scope_chain: + var scope := scope_reference.get_ref() as Control + if ( + scope == null + or not is_instance_valid(scope) + or not scope.is_inside_tree() + ): + continue + if not scope.is_visible_in_tree(): + _scope_chain.clear() + return + var replacement := _best_replacement_in(scope) + if replacement != null: + replacement.grab_focus() + else: + _scope_chain.clear() + return + _scope_chain.clear() + + +func _best_replacement_in(scope: Control) -> Control: + var best: Control = null + var best_score: float = INF + for node: Node in scope.find_children("*", "Control", true, false): + var candidate := node as Control + if not _is_focusable(candidate): + continue + var distance: float = candidate.get_global_rect().get_center().distance_squared_to( + _last_focus_center + ) + if not _last_focus_key.is_empty() and _semantic_key(candidate) == _last_focus_key: + distance -= SEMANTIC_MATCH_BONUS + if distance < best_score: + best = candidate + best_score = distance + return best + + +func _is_focusable(control: Control) -> bool: + if ( + control == null + or not control.is_inside_tree() + or not control.is_visible_in_tree() + or control.focus_mode not in [Control.FOCUS_CLICK, Control.FOCUS_ALL] + ): + return false + var button := control as BaseButton + return button == null or not button.disabled + + +func _semantic_key(control: Control) -> String: + if control.has_meta(&"controller_focus_key"): + return str(control.get_meta(&"controller_focus_key")) + var button := control as Button + var label: String = button.text if button != null else "" + var tooltip: String = control.tooltip_text + tooltip = tooltip.trim_suffix(" (show variants)") + tooltip = tooltip.trim_suffix(" (hide variants)") + return "%s|%s|%s" % [control.get_class(), label, tooltip] + + +func _leave_focus_navigation(release_focus: bool = true) -> void: + _controller_active = false + _focus_navigation_active = false + _recovery_generation += 1 + _scope_chain.clear() + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner == null or _keeps_pointer_focus(focus_owner): + return + if _is_focusable(focus_owner): + _pending_focus = weakref(focus_owner) + if release_focus: + _release_focus_if_inactive.call_deferred(focus_owner) + + +func _release_focus_if_inactive(control: Control) -> void: + if ( + _focus_navigation_active + or _pointer_button_down + or control == null + or not is_instance_valid(control) + or get_viewport().gui_get_focus_owner() != control + or _keeps_pointer_focus(control) + ): + return + get_viewport().gui_release_focus() + + +func _release_current_pointer_focus() -> void: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner != null: + _release_focus_if_inactive(focus_owner) + + +func _request_pending_focus() -> void: + if ( + not _focus_navigation_active + or _pending_focus == null + or get_viewport().gui_get_focus_owner() != null + ): + return + _restore_pending_focus.call_deferred(_recovery_generation) + + +func _restore_pending_focus(generation: int) -> void: + if ( + generation != _recovery_generation + or not _focus_navigation_active + or _pending_focus == null + or get_viewport().gui_get_focus_owner() != null + ): + return + var target := _pending_focus.get_ref() as Control + if not _is_focusable(target): + _pending_focus = null + return + _pending_focus = null + target.grab_focus() + + +func _is_keyboard_navigation_event(event: InputEvent) -> bool: + if event.is_action_pressed(&"open_backpack"): + return false + if ( + event.is_action_pressed(&"ui_cancel") + and get_viewport().gui_get_focus_owner() == null + ): + return false + for action: StringName in KEYBOARD_NAVIGATION_ACTIONS: + if event.is_action_pressed(action): + return true + return false + + +func _keeps_pointer_focus(control: Control) -> bool: + return control is LineEdit or control is TextEdit diff --git a/ui/controller_focus_recovery.gd.uid b/ui/controller_focus_recovery.gd.uid new file mode 100644 index 0000000..b7f8ce9 --- /dev/null +++ b/ui/controller_focus_recovery.gd.uid @@ -0,0 +1 @@ +uid://c7cw61ethr3pt diff --git a/ui/controller_mapping_panel.gd b/ui/controller_mapping_panel.gd new file mode 100644 index 0000000..42fdf92 --- /dev/null +++ b/ui/controller_mapping_panel.gd @@ -0,0 +1,566 @@ +class_name ControllerMappingPanel +extends Control + +signal closed + +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const UtilityPageStyleType = preload("res://ui/utility_page_style.gd") +const CAPTURE_NEUTRAL_SECONDS: float = 0.22 +const CANCEL_COMBO_HOLD_SECONDS: float = 1.25 + +var _mapping_manager: ControllerMappingManagerType +var _binding_buttons: Dictionary = {} +var _controller_label: Label +var _instruction_label: Label +var _progress_label: Label +var _auto_map_button: Button +var _reset_button: Button +var _close_button: Button +var _capturing_role: StringName = &"" +var _auto_map_active: bool = false +var _auto_map_index: int = -1 +var _auto_map_draft: Dictionary = {} +var _capture_device_id: int = 0 +var _waiting_for_neutral: bool = false +var _neutral_elapsed: float = 0.0 +var _cancel_left_bumper_pressed: bool = false +var _cancel_right_bumper_pressed: bool = false +var _cancel_combo_elapsed: float = 0.0 + + +func _ready() -> void: + set_process_input(true) + set_process(true) + _build_interface() + hide() + + +func setup(mapping_manager: ControllerMappingManagerType) -> void: + _mapping_manager = mapping_manager + if not _mapping_manager.active_controller_changed.is_connected( + _on_active_controller_changed + ): + _mapping_manager.active_controller_changed.connect( + _on_active_controller_changed + ) + if not _mapping_manager.active_profile_changed.is_connected( + _refresh_bindings + ): + _mapping_manager.active_profile_changed.connect(_refresh_bindings) + if not _mapping_manager.controller_input_observed.is_connected( + _on_controller_input_observed + ): + _mapping_manager.controller_input_observed.connect( + _on_controller_input_observed + ) + _refresh_bindings() + + +func open_panel() -> void: + if _mapping_manager == null: + return + _cancel_capture() + _mapping_manager.recalibrate_active_device() + show() + _refresh_bindings() + _auto_map_button.grab_focus() + + +func is_open() -> bool: + return visible + + +func is_capturing() -> bool: + return visible and not _capturing_role.is_empty() + + +func request_back() -> void: + if not visible: + return + if not _capturing_role.is_empty(): + _cancel_capture() + return + close_panel() + + +func close_panel() -> void: + _cancel_capture() + hide() + closed.emit() + + +func _input(event: InputEvent) -> void: + if not visible or _mapping_manager == null: + return + if _capturing_role.is_empty(): + return + var key_event := event as InputEventKey + if ( + key_event != null + and key_event.pressed + and not key_event.echo + and key_event.keycode == KEY_ESCAPE + ): + get_viewport().set_input_as_handled() + _cancel_capture() + _progress_label.text = "controller mapping cancelled" + return + + +func _process(delta: float) -> void: + if ( + not visible + or _mapping_manager == null + or _capturing_role.is_empty() + ): + return + if _cancel_left_bumper_pressed and _cancel_right_bumper_pressed: + _cancel_combo_elapsed += delta + _progress_label.text = "keep holding both bumpers to cancel" + if _cancel_combo_elapsed >= CANCEL_COMBO_HOLD_SECONDS: + _cancel_capture() + _progress_label.text = "controller mapping cancelled" + return + _cancel_combo_elapsed = 0.0 + if _waiting_for_neutral: + if not _mapping_manager.are_capture_inputs_neutral(_capture_device_id): + _neutral_elapsed = 0.0 + return + _neutral_elapsed += delta + if _neutral_elapsed < CAPTURE_NEUTRAL_SECONDS: + return + _waiting_for_neutral = false + _neutral_elapsed = 0.0 + _refresh_capture_prompt() + return + var pressed_button: int = _mapping_manager.get_pressed_capture_button( + _capture_device_id + ) + if pressed_button < 0: + return + var button_event := InputEventJoypadButton.new() + button_event.device = _capture_device_id + button_event.button_index = pressed_button as JoyButton + button_event.pressed = true + _try_capture_event(button_event) + + +func _on_controller_input_observed(event: InputEvent) -> void: + if not visible or _mapping_manager == null: + return + if _capturing_role.is_empty(): + return + get_viewport().set_input_as_handled() + var button_event := event as InputEventJoypadButton + var motion_event := event as InputEventJoypadMotion + var event_device: int = -1 + if button_event != null: + event_device = button_event.device + elif motion_event != null: + event_device = motion_event.device + else: + return + if event_device != _capture_device_id: + return + _track_cancel_combo_button(button_event) + if _waiting_for_neutral: + return + _try_capture_event(event) + + +func _try_capture_event(event: InputEvent) -> void: + var binding: Dictionary = _mapping_manager.binding_from_event( + _capturing_role, + event, + ) + if binding.is_empty(): + return + if not _mapping_manager.validate_binding(_capturing_role, binding): + return + _accept_captured_binding(binding) + + +func _build_interface() -> void: + set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + mouse_filter = Control.MOUSE_FILTER_STOP + z_index = 100 + UtilityPageStyleType.apply_page(self) + + var frame := PanelContainer.new() + frame.set_anchors_preset(Control.PRESET_FULL_RECT) + frame.offset_left = 18.0 + frame.offset_top = 18.0 + frame.offset_right = -18.0 + frame.offset_bottom = -18.0 + frame.add_theme_stylebox_override( + "panel", + UtilityPageStyleType.rounded_style( + UtilityPageStyleType.OCEAN_PANEL_DEEP, + 20, + ), + ) + add_child(frame) + + var outer_margin := MarginContainer.new() + outer_margin.add_theme_constant_override("margin_left", 24) + outer_margin.add_theme_constant_override("margin_top", 20) + outer_margin.add_theme_constant_override("margin_right", 24) + outer_margin.add_theme_constant_override("margin_bottom", 20) + frame.add_child(outer_margin) + + var page := VBoxContainer.new() + page.add_theme_constant_override("separation", 12) + outer_margin.add_child(page) + + var heading_row := HBoxContainer.new() + heading_row.add_theme_constant_override("separation", 12) + page.add_child(heading_row) + + var title := Label.new() + title.text = "controller mapping" + title.add_theme_font_size_override("font_size", 28) + title.add_theme_color_override( + "font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY + ) + title.size_flags_horizontal = Control.SIZE_EXPAND_FILL + heading_row.add_child(title) + + _controller_label = Label.new() + _controller_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + _controller_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _controller_label.add_theme_font_size_override("font_size", 15) + _controller_label.add_theme_color_override( + "font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY + ) + heading_row.add_child(_controller_label) + + _instruction_label = Label.new() + _instruction_label.text = ( + "auto-map walks through NETfishing's controller actions. " + + "select any row afterward to override it; the mapped back " + + "control cancels capture." + ) + _instruction_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + _instruction_label.add_theme_font_size_override("font_size", 15) + _instruction_label.add_theme_color_override( + "font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY + ) + page.add_child(_instruction_label) + + _progress_label = Label.new() + _progress_label.custom_minimum_size.y = 34.0 + _progress_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _progress_label.add_theme_font_size_override("font_size", 18) + _progress_label.add_theme_color_override( + "font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY + ) + page.add_child(_progress_label) + + var scroll := ScrollContainer.new() + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + page.add_child(scroll) + + var role_grid := GridContainer.new() + role_grid.columns = 2 + role_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + role_grid.add_theme_constant_override("h_separation", 12) + role_grid.add_theme_constant_override("v_separation", 7) + scroll.add_child(role_grid) + + for role: StringName in ControllerMappingManagerType.ROLE_ORDER: + var role_label := Label.new() + role_label.text = ControllerMappingManagerType.ROLE_LABELS.get( + role, + str(role), + ) + role_label.custom_minimum_size = Vector2(330.0, 42.0) + role_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + role_label.add_theme_font_size_override("font_size", 16) + role_label.add_theme_color_override( + "font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY + ) + role_grid.add_child(role_label) + + var binding_button := Button.new() + binding_button.custom_minimum_size = Vector2(210.0, 42.0) + binding_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + binding_button.focus_mode = Control.FOCUS_ALL + binding_button.tooltip_text = ( + "change " + str(ControllerMappingManagerType.ROLE_LABELS.get( + role, + str(role), + )) + ) + UtilityPageStyleType.apply_compact_ocean_button(binding_button) + binding_button.pressed.connect(_begin_manual_capture.bind(role)) + role_grid.add_child(binding_button) + _binding_buttons[role] = binding_button + + var footer := HBoxContainer.new() + footer.alignment = BoxContainer.ALIGNMENT_END + footer.add_theme_constant_override("separation", 10) + page.add_child(footer) + + _auto_map_button = Button.new() + _auto_map_button.text = "auto-map controller" + _auto_map_button.tooltip_text = ( + "walk through every input on a standard xbox-style controller" + ) + UtilityPageStyleType.apply_ocean_button(_auto_map_button) + _auto_map_button.pressed.connect(_begin_auto_map) + footer.add_child(_auto_map_button) + + _reset_button = Button.new() + _reset_button.text = "restore defaults" + _reset_button.tooltip_text = "remove this controller's custom mapping" + UtilityPageStyleType.apply_ocean_button(_reset_button) + _reset_button.pressed.connect(_reset_mapping) + footer.add_child(_reset_button) + + _close_button = Button.new() + _close_button.text = "done" + UtilityPageStyleType.apply_ocean_button(_close_button) + _close_button.pressed.connect(close_panel) + footer.add_child(_close_button) + + +func _begin_auto_map() -> void: + if _mapping_manager == null: + return + _auto_map_active = true + _auto_map_index = 0 + _auto_map_draft = {} + _reset_cancel_combo() + _capture_device_id = _mapping_manager.get_active_device_id() + _set_capture_role(ControllerMappingManagerType.ROLE_ORDER[_auto_map_index]) + + +func _begin_manual_capture(role: StringName) -> void: + _auto_map_active = false + _auto_map_index = -1 + _auto_map_draft.clear() + _reset_cancel_combo() + _capture_device_id = _mapping_manager.get_active_device_id() + _set_capture_role(role) + + +func _set_capture_role(role: StringName) -> void: + _capturing_role = role + _waiting_for_neutral = true + _neutral_elapsed = 0.0 + _set_action_buttons_disabled(true) + _progress_label.text = "release all controller inputs" + + +func _refresh_capture_prompt() -> void: + if _capturing_role.is_empty(): + return + if _auto_map_active: + _progress_label.text = ( + "step %d of %d: %s\n" + + "hold both bumpers for %.2f seconds to cancel" + ) % [ + _auto_map_index + 1, + ControllerMappingManagerType.ROLE_ORDER.size(), + _mapping_manager.get_role_prompt(_capturing_role), + CANCEL_COMBO_HOLD_SECONDS, + ] + return + var input_instruction: String = "press any controller button" + if _mapping_manager.role_expects_axis(_capturing_role): + input_instruction = "move any controller axis" + elif _mapping_manager.role_accepts_axis(_capturing_role): + input_instruction = "press any button or move any controller axis" + _progress_label.text = "%s for %s" % [ + input_instruction, + _mapping_manager.get_role_label(_capturing_role), + ] + + +func _accept_captured_binding(binding: Dictionary) -> void: + if _auto_map_active: + _auto_map_draft[str(_capturing_role)] = binding.duplicate(true) + _auto_map_index += 1 + if _auto_map_index < ControllerMappingManagerType.ROLE_ORDER.size(): + _set_capture_role( + ControllerMappingManagerType.ROLE_ORDER[_auto_map_index] + ) + return + var profile_saved: bool = _mapping_manager.replace_active_bindings( + _auto_map_draft + ) + _cancel_capture() + _progress_label.text = ( + "controller mapped successfully" + if profile_saved else "could not save the controller mapping" + ) + _refresh_bindings() + return + var role: StringName = _capturing_role + var binding_saved: bool = _mapping_manager.set_binding(role, binding) + _cancel_capture() + _progress_label.text = ( + "updated " + _mapping_manager.get_role_label(role) + if binding_saved else "could not save that controller input" + ) + _refresh_bindings() + +func _cancel_capture() -> void: + _capturing_role = &"" + _auto_map_active = false + _auto_map_index = -1 + _auto_map_draft.clear() + _waiting_for_neutral = false + _neutral_elapsed = 0.0 + _reset_cancel_combo() + _set_action_buttons_disabled(false) + if _progress_label != null: + _progress_label.text = "" + + +func _track_cancel_combo_button(button_event: InputEventJoypadButton) -> void: + if button_event == null: + return + match button_event.button_index: + JOY_BUTTON_LEFT_SHOULDER: + _cancel_left_bumper_pressed = button_event.pressed + JOY_BUTTON_RIGHT_SHOULDER: + _cancel_right_bumper_pressed = button_event.pressed + _: + return + if not ( + _cancel_left_bumper_pressed and _cancel_right_bumper_pressed + ): + _cancel_combo_elapsed = 0.0 + + +func _reset_cancel_combo() -> void: + _cancel_left_bumper_pressed = false + _cancel_right_bumper_pressed = false + _cancel_combo_elapsed = 0.0 + + +func _set_action_buttons_disabled(disabled: bool) -> void: + if _auto_map_button != null: + _auto_map_button.disabled = disabled + if _reset_button != null: + _reset_button.disabled = disabled + for button_value: Variant in _binding_buttons.values(): + var button := button_value as Button + if button != null: + button.disabled = disabled + + +func _reset_mapping() -> void: + if _mapping_manager == null: + return + var reset: bool = _mapping_manager.reset_active_profile() + _progress_label.text = ( + "restored the project controller defaults" + if reset else "could not restore controller defaults" + ) + _refresh_bindings() + + +func _refresh_bindings() -> void: + if _mapping_manager == null or _controller_label == null: + return + _controller_label.text = _mapping_manager.get_active_controller_name() + var bindings: Dictionary = _mapping_manager.get_active_bindings() + for role: StringName in ControllerMappingManagerType.ROLE_ORDER: + var button := _binding_buttons.get(role) as Button + if button == null: + continue + var raw_binding: Variant = bindings.get(str(role), {}) + button.text = ( + _mapping_manager.binding_label(raw_binding as Dictionary) + if typeof(raw_binding) == TYPE_DICTIONARY else "unmapped" + ) + var conflict_role: StringName = _find_binding_conflict( + role, + raw_binding as Dictionary, + bindings, + ) + _apply_binding_button_style(button, not conflict_role.is_empty()) + button.tooltip_text = ( + "warning: also assigned to " + + _mapping_manager.get_role_label(conflict_role) + if not conflict_role.is_empty() + else "change " + _mapping_manager.get_role_label(role) + ) + _reset_button.disabled = not _mapping_manager.has_custom_mapping() + + +func _find_binding_conflict( + role: StringName, + binding: Dictionary, + bindings: Dictionary, +) -> StringName: + if binding.is_empty(): + return &"" + for other_role: StringName in ControllerMappingManagerType.ROLE_ORDER: + if other_role == role: + continue + var raw_other: Variant = bindings.get(str(other_role), {}) + if typeof(raw_other) != TYPE_DICTIONARY: + continue + var other := raw_other as Dictionary + if _bindings_conflict(role, binding, other_role, other): + return other_role + return &"" + + +func _bindings_conflict( + first_role: StringName, + first: Dictionary, + second_role: StringName, + second: Dictionary, +) -> bool: + var binding_kind: String = str(first.get("kind", "")) + if binding_kind != str(second.get("kind", "")): + return false + if binding_kind == "button": + return int(first.get("button", -1)) == int( + second.get("button", -1) + ) + if binding_kind != "axis": + return false + if int(first.get("axis", -1)) != int(second.get("axis", -1)): + return false + var opposite_trigger_halves: bool = ( + first_role in ControllerMappingManagerType.TRIGGER_ROLES + and second_role in ControllerMappingManagerType.TRIGGER_ROLES + and signf(float(first.get("direction", 0.0))) + != signf(float(second.get("direction", 0.0))) + ) + return not opposite_trigger_halves + + +func _apply_binding_button_style(button: Button, has_conflict: bool) -> void: + UtilityPageStyleType.apply_compact_ocean_button(button) + if not has_conflict: + return + var colors: Dictionary = { + &"normal": UtilityPageStyleType.OCEAN_DANGER, + &"hover": UtilityPageStyleType.OCEAN_DANGER.lightened(0.12), + &"pressed": UtilityPageStyleType.OCEAN_DANGER.darkened(0.12), + &"focus": UtilityPageStyleType.OCEAN_DANGER.lightened(0.12), + } + for state: StringName in colors: + var style := UtilityPageStyleType.ocean_button_style( + colors[state] as Color + ) + style.content_margin_left = 10.0 + style.content_margin_right = 10.0 + style.content_margin_top = 4.0 + style.content_margin_bottom = 4.0 + button.add_theme_stylebox_override(state, style) + + +func _on_active_controller_changed(_controller_name: String) -> void: + if not _capturing_role.is_empty(): + _cancel_capture() + _refresh_bindings() diff --git a/ui/controller_mapping_panel.gd.uid b/ui/controller_mapping_panel.gd.uid new file mode 100644 index 0000000..41f5e25 --- /dev/null +++ b/ui/controller_mapping_panel.gd.uid @@ -0,0 +1 @@ +uid://b0rl6lgpc0h85 diff --git a/ui/controller_virtual_cursor.gd b/ui/controller_virtual_cursor.gd new file mode 100644 index 0000000..f56bb3e --- /dev/null +++ b/ui/controller_virtual_cursor.gd @@ -0,0 +1,40 @@ +class_name ControllerVirtualCursor +extends Control + +const CURSOR_SIZE: Vector2 = Vector2(28.0, 28.0) +const OUTER_COLOR: Color = Color(0.02, 0.10, 0.14, 0.96) +const INNER_COLOR: Color = Color(0.34, 0.86, 0.91, 1.0) +const CENTER_COLOR: Color = Color(0.96, 1.0, 1.0, 1.0) + + +func _ready() -> void: + size = CURSOR_SIZE + mouse_filter = Control.MOUSE_FILTER_IGNORE + focus_mode = Control.FOCUS_NONE + visible = false + queue_redraw() + + +func set_pointer_position(pointer_position: Vector2) -> void: + position = pointer_position - CURSOR_SIZE * 0.5 + + +func _draw() -> void: + var center: Vector2 = CURSOR_SIZE * 0.5 + draw_circle(center, 8.0, OUTER_COLOR) + draw_circle(center, 5.0, INNER_COLOR) + draw_circle(center, 1.75, CENTER_COLOR) + draw_line(Vector2(center.x, 0.0), Vector2(center.x, 6.0), CENTER_COLOR, 2.0) + draw_line( + Vector2(center.x, CURSOR_SIZE.y - 6.0), + Vector2(center.x, CURSOR_SIZE.y), + CENTER_COLOR, + 2.0, + ) + draw_line(Vector2(0.0, center.y), Vector2(6.0, center.y), CENTER_COLOR, 2.0) + draw_line( + Vector2(CURSOR_SIZE.x - 6.0, center.y), + Vector2(CURSOR_SIZE.x, center.y), + CENTER_COLOR, + 2.0, + ) diff --git a/ui/controller_virtual_cursor.gd.uid b/ui/controller_virtual_cursor.gd.uid new file mode 100644 index 0000000..309a5c4 --- /dev/null +++ b/ui/controller_virtual_cursor.gd.uid @@ -0,0 +1 @@ +uid://dehegl7ccn057 diff --git a/ui/currency_presentation.gd b/ui/currency_presentation.gd new file mode 100644 index 0000000..22fb32a --- /dev/null +++ b/ui/currency_presentation.gd @@ -0,0 +1,28 @@ +class_name CurrencyPresentation +extends RefCounted + +const ICON_PATH: String = ( + "res://items/icons/shop/32_currency.png" +) +const AMOUNT_SCENE: PackedScene = preload( + "res://ui/components/currency_amount.tscn" +) + + +static func instantiate_amount( + value: int, + icon_size: float = 18.0, +) -> CurrencyAmount: + var display := AMOUNT_SCENE.instantiate() as CurrencyAmount + display.icon_size = icon_size + display.amount = value + return display + + +static func bbcode_amount(value: int, icon_size: int = 18) -> String: + return "[img=%dx%d]%s[/img] %d" % [ + icon_size, + icon_size, + ICON_PATH, + value, + ] diff --git a/ui/currency_presentation.gd.uid b/ui/currency_presentation.gd.uid new file mode 100644 index 0000000..210c2b8 --- /dev/null +++ b/ui/currency_presentation.gd.uid @@ -0,0 +1 @@ +uid://bcvy1l2jqqbmt diff --git a/ui/emote_radial_menu.gd b/ui/emote_radial_menu.gd index 4d869e0..c081e27 100644 --- a/ui/emote_radial_menu.gd +++ b/ui/emote_radial_menu.gd @@ -13,10 +13,22 @@ const SECTOR_COUNT: int = 8 const SIT_SECTOR: int = 0 const RING_RADIUS: float = 172.0 const BUBBLE_SIZE: Vector2 = Vector2(92.0, 88.0) +const CONTROLLER_SELECTION_DEADZONE: float = 0.35 +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) var _buttons: Array[BubbleButton] = [] var _is_open: bool = false var _selected_sector: int = SIT_SECTOR +var _controller_selection_mode: bool = false +var _controller_mapping_manager: ControllerMappingManagerType + + +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager func _ready() -> void: @@ -34,15 +46,15 @@ func _ready() -> void: func handle_input(event: InputEvent, can_open: bool) -> bool: + if not event.is_action("open_emotes"): + return false var key_event: InputEventKey = event as InputEventKey - if key_event == null or key_event.echo: + if key_event != null and key_event.echo: return false - if key_event.physical_keycode != KEY_C: - return false - if key_event.pressed: + if event.is_pressed(): if _is_open or not can_open: return _is_open - open_menu() + open_menu(event is InputEventJoypadButton) return true if not _is_open: return false @@ -53,9 +65,10 @@ func handle_input(event: InputEvent, can_open: bool) -> bool: return true -func open_menu() -> void: +func open_menu(controller_selection: bool = false) -> void: _is_open = true _selected_sector = SIT_SECTOR + _controller_selection_mode = controller_selection visible = true _layout_bubbles() _apply_selection_styles() @@ -74,7 +87,7 @@ func _process(_delta: float) -> void: if not _is_open: return _layout_bubbles() - _update_mouse_selection() + _update_selection() func _layout_bubbles() -> void: @@ -88,10 +101,40 @@ func _layout_bubbles() -> void: bubble.pivot_offset = BUBBLE_SIZE * 0.5 +func _update_selection() -> void: + var stick: Vector2 = _get_selection_stick() + if stick.length() >= CONTROLLER_SELECTION_DEADZONE: + _select_sector_from_offset(stick) + return + if _controller_selection_mode: + return + _update_mouse_selection() + + +func _get_selection_stick() -> Vector2: + if _controller_mapping_manager != null: + return Vector2( + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X + ), + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y + ), + ) + return Vector2( + Input.get_joy_axis(0, JOY_AXIS_RIGHT_X), + Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y), + ) + + func _update_mouse_selection() -> void: var offset: Vector2 = get_local_mouse_position() - size * 0.5 if offset.length() < 24.0: return + _select_sector_from_offset(offset) + + +func _select_sector_from_offset(offset: Vector2) -> void: var angle: float = fposmod(offset.angle() + PI * 0.5 + PI / 8.0, TAU) var sector: int = int(floor(angle / (TAU / float(SECTOR_COUNT)))) if sector == _selected_sector: diff --git a/ui/fishing_shop.gd b/ui/fishing_shop.gd index 79ba3f8..d8678b7 100644 --- a/ui/fishing_shop.gd +++ b/ui/fishing_shop.gd @@ -25,8 +25,23 @@ const ShopInteractionType = preload( "res://world/fishing_shop_interaction.gd" ) const UtilityPageStyleType = preload("res://ui/utility_page_style.gd") +const OrganizerTabType = preload("res://ui/components/organizer_tab.gd") +const UIMotionType = preload("res://ui/ui_motion.gd") +const FALLBACK_SUPPLY_ICON: Texture2D = preload( + "res://ui/icons/pictograms/x_light.png" +) +const ART_KIT_MARKER_ICON: Texture2D = preload( + "res://items/icons/art/art_kit_marker.png" +) +const ART_KIT_MARKER_SHADER: Shader = preload( + "res://ui/art_kit_marker_icon.gdshader" +) +const CURRENCY_ICON: Texture2D = preload( + "res://items/icons/shop/32_currency.png" +) signal menu_visibility_changed(is_open: bool) +signal menu_exit_started signal sell_fish_requested signal shop_cooler_return_requested signal shop_cooler_confirmation_cancel_requested @@ -39,26 +54,56 @@ enum CloseReason { TEARDOWN, } +enum ShopSection { + UPGRADES, + BAIT, + SNACKS, + EQUIPMENT, + ART_SUPPLIES, + SELL_FISH, +} + +const SHOP_SECTION_LABELS: Array[String] = [ + "Upgrades", + "Bait and Lures", + "Snacks", + "Equipment", + "Art Supplies", + "Sell Fish", +] +const SUPPLY_ICON_GRID_COLUMNS: int = 9 +const SUPPLY_ICON_TILE_SIZE := Vector2(72.0, 72.0) +const SUPPLY_ICON_HOST_SIZE := Vector2(72.0, 84.0) +const SUPPLY_ICON_GRID_SEPARATION: int = 8 +const SUPPLY_PRICE_COLOR := Color("c3dfe6") +const SUPPLY_PRICE_FONT_SIZE: int = 15 +const SUPPLY_PRICE_Y: float = 60.0 +const SUPPLY_PRICE_HEIGHT: float = 24.0 +const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0 +const SUPPLY_PRICE_ICON_SIZE: float = 18.0 +const SUPPLY_PRICE_ICON_GAP: float = 3.0 + @onready var _wallet_label: Label = %WalletLabel @onready var _shop_panel: PanelContainer = %ShopPanel +@onready var _shop_panel_margin: MarginContainer = $ShopPanel/Margin +@onready var _shop_panel_layout: VBoxContainer = $ShopPanel/Margin/Layout @onready var _shop_cooler_page: Control = %ShopCoolerPage @onready var _shop_cooler_mount: Control = %ShopCoolerMount -@onready var _back_to_shop_button: Button = %BackToShopButton -@onready var _buy_mode_button: Button = %BuyModeButton -@onready var _sell_mode_button: Button = %SellModeButton -@onready var _feedback: Label = %Feedback -@onready var _reel_level: Label = %ReelLevel -@onready var _reel_effect: Label = %ReelEffect -@onready var _reel_cost: Label = %ReelCost +@onready var _shop_body: HBoxContainer = $ShopPanel/Margin/Layout/Body +@onready var _feedback: RichTextLabel = %Feedback +@onready var _shop_tab_bar: HBoxContainer = %ShopTabBar +@onready var _upgrades_content: VBoxContainer = %Upgrades +@onready var _supplies_content: VBoxContainer = %Supplies +@onready var _stock_title: Label = %StockTitle +@onready var _reel_cost: CurrencyAmount = %ReelCost +@onready var _reel_price_bubble: PanelContainer = %ReelPriceBubble @onready var _reel_purchase: Button = %ReelPurchase -@onready var _barrier_level: Label = %BarrierLevel -@onready var _barrier_effect: Label = %BarrierEffect -@onready var _barrier_cost: Label = %BarrierCost +@onready var _barrier_cost: CurrencyAmount = %BarrierCost +@onready var _barrier_price_bubble: PanelContainer = %BarrierPriceBubble @onready var _barrier_purchase: Button = %BarrierPurchase @onready var _supplies_list: VBoxContainer = %SuppliesList -@onready var _cooler_level: Label = %CoolerLevel -@onready var _cooler_effect: Label = %CoolerEffect -@onready var _cooler_cost: Label = %CoolerCost +@onready var _cooler_cost: CurrencyAmount = %CoolerCost +@onready var _cooler_price_bubble: PanelContainer = %CoolerPriceBubble @onready var _cooler_purchase: Button = %CoolerPurchase var _player: PlayerType @@ -79,40 +124,116 @@ var _snapshot_stored: bool = false var _mouse_snapshot_stored: bool = false var _transaction_in_progress: bool = false var _closing: bool = false +var _close_generation: int = 0 +var _close_tween: Tween var _cooler_page_active: bool = false var _cooler_modal_open: bool = false +var _shop_section: ShopSection = ShopSection.UPGRADES +var _shop_tabs: Array[OrganizerTab] = [] func _ready() -> void: UtilityPageStyleType.apply_page(self) _apply_shop_styles() + _build_shop_tabs() %CloseButton.pressed.connect(close_shop) - _buy_mode_button.pressed.connect(_focus_buy_page) - _sell_mode_button.pressed.connect(_request_shop_cooler) - _back_to_shop_button.pressed.connect(shop_cooler_return_requested.emit) _reel_purchase.pressed.connect(_purchase_reel_speed) _barrier_purchase.pressed.connect(_purchase_barrier_power) _cooler_purchase.pressed.connect(_purchase_cooler_capacity) -func _request_shop_cooler() -> void: +func _request_shop_cooler() -> bool: if _transaction_in_progress: - _feedback.text = "Finish the current transaction first." - return + _set_feedback("Finish the current transaction first.") + return false if not _is_transaction_context_valid(): - _feedback.text = "The fishing shop is no longer available." - return + _set_feedback("The fishing shop is no longer available.") + return false sell_fish_requested.emit() + return _cooler_page_active -func _focus_buy_page() -> void: - _feedback.text = "" - for child: Node in _supplies_list.get_children(): +func _focus_shop_section() -> void: + _set_feedback("") + if _shop_section == ShopSection.UPGRADES: + _reel_purchase.grab_focus() + return + if _shop_section == ShopSection.SELL_FISH: + _shop_tabs[int(ShopSection.SELL_FISH)].grab_focus() + return + for child: Node in _supplies_list.find_children( + "*", "Button", true, false + ): var stock_button := child as Button if stock_button != null and not stock_button.disabled: stock_button.grab_focus() return - _reel_purchase.grab_focus() + var section_index: int = int(_shop_section) + if section_index >= 0 and section_index < _shop_tabs.size(): + _shop_tabs[section_index].grab_focus() + + +func _build_shop_tabs() -> void: + _shop_tab_bar.custom_minimum_size.y = 44.0 + _shop_tab_bar.show() + for section_index: int in range(ShopSection.size()): + if section_index == ShopSection.SELL_FISH: + var tab_group_spacer := Control.new() + tab_group_spacer.name = "SellTabSpacer" + tab_group_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + tab_group_spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE + _shop_tab_bar.add_child(tab_group_spacer) + var tab: OrganizerTab = OrganizerTabType.new() + tab.text = SHOP_SECTION_LABELS[section_index] + tab.palette_index = section_index + tab.custom_minimum_size = Vector2(132.0, 42.0) + tab.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + tab.focus_mode = Control.FOCUS_ALL + tab.mouse_filter = Control.MOUSE_FILTER_STOP + tab.pressed.connect(_select_shop_section.bind(section_index, true)) + _shop_tab_bar.add_child(tab) + tab.show() + _shop_tabs.append(tab) + _select_shop_section(ShopSection.UPGRADES, false) + + +func _select_shop_section(section_index: int, focus_content: bool) -> void: + if _closing: + return + if section_index == int(_shop_section): + if section_index != ShopSection.SELL_FISH: + var showing_upgrades := section_index == ShopSection.UPGRADES + _upgrades_content.visible = showing_upgrades + _supplies_content.visible = not showing_upgrades + _update_shop_tab_selection() + if focus_content: + _focus_shop_section() + return + if section_index == ShopSection.SELL_FISH: + if not _request_shop_cooler(): + _update_shop_tab_selection() + return + _shop_section = ShopSection.SELL_FISH + _update_shop_tab_selection() + return + if _cooler_page_active: + shop_cooler_return_requested.emit() + _shop_section = section_index as ShopSection + var showing_upgrades: bool = _shop_section == ShopSection.UPGRADES + _upgrades_content.visible = showing_upgrades + _supplies_content.visible = not showing_upgrades + _update_shop_tab_selection() + _refresh_supplies() + if focus_content and visible: + _focus_shop_section() + + +func _update_shop_tab_selection() -> void: + for tab_index: int in range(_shop_tabs.size()): + _shop_tabs[tab_index].set_selected( + tab_index == int(_shop_section), + true, + ) func _apply_shop_styles() -> void: @@ -127,29 +248,38 @@ func _apply_shop_styles() -> void: "panel", shop_panel_style ) for panel: PanelContainer in [ - $ShopPanel/Margin/Layout/Body/Upgrades/ReelCard, - $ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard, - $ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard, + _reel_price_bubble, + _barrier_price_bubble, + _cooler_price_bubble, ]: - panel.add_theme_stylebox_override( - "panel", UtilityPageStyleType.row_style() + var price_style := UtilityPageStyleType.rounded_style( + UtilityPageStyleType.OCEAN_FIELD, + 18, ) + price_style.content_margin_left = 8.0 + price_style.content_margin_right = 8.0 + price_style.content_margin_top = 2.0 + price_style.content_margin_bottom = 2.0 + panel.add_theme_stylebox_override( + "panel", price_style + ) + for price: CurrencyAmount in [ + _reel_cost, + _barrier_cost, + _cooler_cost, + ]: + var amount_label := price.get_amount_label() + amount_label.add_theme_color_override( + "font_color", SUPPLY_PRICE_COLOR + ) + amount_label.add_theme_font_size_override("font_size", 18) for button: BaseButton in [ %CloseButton, - _buy_mode_button, - _sell_mode_button, _reel_purchase, _barrier_purchase, _cooler_purchase, - _back_to_shop_button, ]: UtilityPageStyleType.apply_ocean_button(button) - _buy_mode_button.add_theme_stylebox_override( - "normal", - UtilityPageStyleType.ocean_button_style( - UtilityPageStyleType.OCEAN_SELECTED - ), - ) _feedback.add_theme_color_override( "font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY ) @@ -219,7 +349,10 @@ func open_shop() -> bool: or _buyer.id != MAIN_SHOP_BUYER_ID ): return false + _cancel_close_tween() + _close_generation += 1 _closing = false + modulate.a = 1.0 _transaction_in_progress = ( _network_shop != null and _network_shop.is_local_purchase_pending() @@ -233,11 +366,13 @@ func open_shop() -> bool: _player.set_camera_input_enabled(false) _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE - _feedback.text = "" + _set_feedback("") deactivate_shop_cooler_page() show() + _shop_tab_bar.show() + _select_shop_section(ShopSection.UPGRADES, false) _refresh_all() - _buy_mode_button.grab_focus() + _reel_purchase.grab_focus() menu_visibility_changed.emit(true) return true @@ -245,11 +380,13 @@ func open_shop() -> bool: func consume_escape() -> bool: if not visible: return false + if _closing: + return true if _cooler_page_active: if _cooler_modal_open: shop_cooler_confirmation_cancel_requested.emit() else: - shop_cooler_return_requested.emit() + _select_shop_section(ShopSection.UPGRADES, true) return true close_shop() return true @@ -261,24 +398,42 @@ func get_shop_cooler_mount() -> Control: func activate_shop_cooler_page() -> void: _cooler_page_active = true - _shop_panel.hide() + _shop_panel.show() + _set_shop_panel_background_pointer_blocking(false) + _shop_body.hide() + _feedback.hide() _shop_cooler_page.show() - _back_to_shop_button.disabled = false func deactivate_shop_cooler_page() -> void: + if visible: + _shop_tab_bar.show() _cooler_page_active = false _cooler_modal_open = false - _back_to_shop_button.disabled = false + _set_shop_panel_background_pointer_blocking(true) + for tab: OrganizerTab in _shop_tabs: + tab.disabled = false _shop_cooler_page.hide() _shop_panel.show() - if visible: - _buy_mode_button.grab_focus() + _shop_body.show() + _feedback.show() + + +func _set_shop_panel_background_pointer_blocking(blocking: bool) -> void: + var mouse_filter := ( + Control.MOUSE_FILTER_STOP + if blocking + else Control.MOUSE_FILTER_IGNORE + ) + _shop_panel.mouse_filter = mouse_filter + _shop_panel_margin.mouse_filter = mouse_filter + _shop_panel_layout.mouse_filter = mouse_filter func set_shop_cooler_modal_open(is_open: bool) -> void: _cooler_modal_open = is_open - _back_to_shop_button.disabled = is_open + for tab: OrganizerTab in _shop_tabs: + tab.disabled = is_open func close_shop( @@ -287,10 +442,47 @@ func close_shop( ) -> void: if not visible: return + if _closing: + if reason in [ + CloseReason.WATER_RECOVERY, + CloseReason.SESSION_END, + CloseReason.TEARDOWN, + ]: + _finish_close(reason, restore_controls, _close_generation) + return _closing = true _transaction_in_progress = false + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() + menu_exit_started.emit() + _close_generation += 1 + var generation: int = _close_generation + if reason in [CloseReason.USER, CloseReason.RANGE_EXIT]: + _close_tween = create_tween() + _close_tween.tween_property( + self, + "modulate:a", + 0.0, + UIMotionType.PLAYER_MENU_EXIT_DURATION, + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + _close_tween.finished.connect( + _finish_close.bind(reason, restore_controls, generation) + ) + return + _finish_close(reason, restore_controls, generation) + + +func _finish_close( + reason: CloseReason, + restore_controls: bool, + generation: int, +) -> void: + if generation != _close_generation or not visible: + return + _cancel_close_tween() hide() - get_viewport().gui_release_focus() + modulate.a = 1.0 if _fishing_spot != null and is_instance_valid(_fishing_spot): _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) if ( @@ -308,6 +500,13 @@ func close_shop( deactivate_shop_cooler_page() +func _cancel_close_tween() -> void: + if _close_tween == null: + return + _close_tween.kill() + _close_tween = null + + func close_for_range_exit() -> void: close_shop(CloseReason.RANGE_EXIT) @@ -340,9 +539,9 @@ func _refresh_all() -> void: func _refresh_wallet() -> void: _wallet_label.text = ( - "wallet: $%d" % _wallet.get_balance() + str(_wallet.get_balance()) if _wallet != null - else "wallet: $0" + else "0" ) @@ -351,7 +550,6 @@ func _refresh_upgrades() -> void: return var reel_level: int = _upgrades.get_reel_speed_level() var reel_cost: int = _upgrades.get_next_reel_speed_cost() - _reel_level.text = "level %d" % reel_level _reel_purchase.disabled = ( reel_cost < 0 or _transaction_in_progress @@ -360,27 +558,30 @@ func _refresh_upgrades() -> void: or not _network_shop.can_request_purchase() or not _upgrades.can_purchase_reel_speed(_wallet) ) - _reel_purchase.tooltip_text = ( - "Purchases are unavailable in this session." - if _network_shop == null or not _network_shop.can_request_purchase() - else "" - ) + var reel_effect: String if reel_cost < 0: - _reel_effect.text = "%.2f×" % _upgrades.get_reel_speed_multiplier() - _reel_cost.text = "max" + reel_effect = "%.2f× reel speed · maximum level" % ( + _upgrades.get_reel_speed_multiplier() + ) + _reel_price_bubble.hide() else: - _reel_effect.text = ( + _reel_price_bubble.show() + reel_effect = ( "%.2f× → %.2f×" % [ _upgrades.get_reel_speed_multiplier(), 1.0 + float(reel_level + 1) * 0.10, ] ) - _reel_cost.text = "$%d" % reel_cost - _reel_purchase.text = "max" if reel_cost < 0 else "purchase" + _reel_cost.set_amount(reel_cost) + _reel_purchase.tooltip_text = _upgrade_tooltip( + "reel speed", + reel_level, + reel_effect, + ) + _reel_purchase.accessibility_name = _reel_purchase.tooltip_text var barrier_level: int = _upgrades.get_barrier_power_level() var barrier_cost: int = _upgrades.get_next_barrier_power_cost() - _barrier_level.text = "level %d" % barrier_level _barrier_purchase.disabled = ( barrier_cost < 0 or _transaction_in_progress @@ -389,20 +590,39 @@ func _refresh_upgrades() -> void: or not _network_shop.can_request_purchase() or not _upgrades.can_purchase_barrier_power(_wallet) ) - _barrier_purchase.tooltip_text = _reel_purchase.tooltip_text + var barrier_effect: String if barrier_cost < 0: - _barrier_effect.text = "%d damage" % _upgrades.get_barrier_damage() - _barrier_cost.text = "max" + barrier_effect = "%d damage · maximum level" % ( + _upgrades.get_barrier_damage() + ) + _barrier_price_bubble.hide() else: - _barrier_effect.text = ( + _barrier_price_bubble.show() + barrier_effect = ( "%d damage → %d damage" % [ _upgrades.get_barrier_damage(), barrier_level + 2, ] ) - _barrier_cost.text = "$%d" % barrier_cost - _barrier_purchase.text = "max" if barrier_cost < 0 else "purchase" + _barrier_cost.set_amount(barrier_cost) + _barrier_purchase.tooltip_text = _upgrade_tooltip( + "rod power", + barrier_level, + barrier_effect, + ) + _barrier_purchase.accessibility_name = _barrier_purchase.tooltip_text + + +func _upgrade_tooltip( + upgrade_name: String, + level: int, + effect: String, +) -> String: + var tooltip := "%s\nlevel %d\n%s" % [upgrade_name, level, effect] + if _network_shop == null or not _network_shop.can_request_purchase(): + tooltip += "\nPurchases are unavailable in this session." + return tooltip func _refresh_supplies() -> void: @@ -411,51 +631,160 @@ func _refresh_supplies() -> void: for child: Node in _supplies_list.get_children(): _supplies_list.remove_child(child) child.queue_free() - _add_stock_section("supplies") - for item_id: StringName in FishingShopStockType.get_stock_item_ids(): + if _shop_section in [ShopSection.UPGRADES, ShopSection.SELL_FISH]: + return + _stock_title.text = SHOP_SECTION_LABELS[int(_shop_section)] + var stock_item_ids: Array[StringName] = ( + FishingShopStockType.get_stock_item_ids() + ) + if _shop_section == ShopSection.BAIT: + var grouped_item_ids: Array[StringName] = [] + for item_id: StringName in stock_item_ids: + var bait_item: ItemDataType = _item_catalog.get_item_by_id(item_id) + if bait_item != null and bait_item.is_bait(): + grouped_item_ids.append(item_id) + for item_id: StringName in stock_item_ids: + var lure_item: ItemDataType = _item_catalog.get_item_by_id(item_id) + if lure_item != null and lure_item.is_lure(): + grouped_item_ids.append(item_id) + stock_item_ids = grouped_item_ids + var current_stock_group: StringName = StringName() + var current_stock_grid: GridContainer + if _shop_section in [ShopSection.SNACKS, ShopSection.EQUIPMENT]: + current_stock_grid = _add_stock_icon_grid() + for item_id: StringName in stock_item_ids: var item: ItemDataType = _item_catalog.get_item_by_id(item_id) - if item == null: + if item == null or not _item_belongs_in_current_section(item): continue + if _shop_section == ShopSection.BAIT: + var stock_group: StringName = ( + &"bait" if item.is_bait() else &"lures" + ) + if stock_group != current_stock_group: + _add_stock_section(str(stock_group)) + current_stock_grid = _add_stock_icon_grid() + current_stock_group = stock_group var button := Button.new() button.custom_minimum_size = Vector2(195, 54) - button.icon = item.icon + button.icon = item.icon if item.icon != null else FALLBACK_SUPPLY_ICON button.expand_icon = true button.alignment = HORIZONTAL_ALIGNMENT_LEFT - button.text = "%s\n$%d • owned %d" % [ + var owned: int = _bag.get_quantity(item_id) + var bait_topoff: bool = FishingShopStockType.is_bait_topoff(item_id) + var bait_unlocked: bool = ( + not bait_topoff or _bag.is_bait_unlocked(item_id) + ) + var quantity: int = FishingShopStockType.get_purchase_quantity( + item_id, owned, item.max_stack + ) + var total_cost: int = FishingShopStockType.get_purchase_cost( + item_id, quantity, bait_unlocked + ) + var use_icon_tile: bool = ( + current_stock_grid != null + ) + button.text = "%s\nowned %d" % [ item.display_name, - FishingShopStockType.get_price(item_id), - _bag.get_quantity(item_id), + owned, ] - button.tooltip_text = item.description + var item_tooltip_text: String = item.description + if bait_topoff: + if use_icon_tile: + button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE + button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.text = "" + else: + button.text = ( + "%s\nunlock and fill" % item.display_name + if not bait_unlocked + else "%s\n%d/%d owned" % [ + item.display_name, + owned, + item.max_stack, + ] + ) + item_tooltip_text = ( + "%s\nunlock and fill to %d/%d\n%s" % [ + item.display_name, + item.max_stack, + item.max_stack, + item.description, + ] + if not bait_unlocked + else "%s\n%d/%d owned\n%s" % [ + item.display_name, + owned, + item.max_stack, + item.description, + ] + ) + elif use_icon_tile: + button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE + button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.text = "" + item_tooltip_text = "%s\nowned %d\n%s" % [ + item.display_name, + owned, + item.description, + ] button.disabled = ( _transaction_in_progress or _closing or _network_shop == null or not _network_shop.can_request_purchase() - or not _bag.can_add_item(item_id, 1) - or not _wallet.can_afford( - FishingShopStockType.get_price(item_id) - ) + or quantity <= 0 + or total_cost < 0 + or not _bag.can_add_item(item_id, quantity) + or not _wallet.can_afford(total_cost) ) button.tooltip_text = ( "Purchases are unavailable in this session." if _network_shop == null or not _network_shop.can_request_purchase() - else item.description + else item_tooltip_text ) UtilityPageStyleType.apply_ocean_button(button) button.pressed.connect(_purchase_supply.bind(item_id)) - _supplies_list.add_child(button) - _add_stock_section("art kit") - _add_art_kit_button() - _add_stock_section("markers") - for product_id: StringName in ArtShopStockType.MARKER_PRODUCTS: - _add_art_upgrade_button(product_id) - _add_stock_section("brushes") - for product_id: StringName in ArtShopStockType.BRUSH_PRODUCTS: - _add_art_upgrade_button(product_id) - _add_stock_section("grids") - for product_id: StringName in ArtShopStockType.GRID_PRODUCTS: - _add_art_upgrade_button(product_id) + if current_stock_grid != null: + _add_supply_icon_tile( + current_stock_grid, + button, + ( + total_cost + if bait_topoff and not bait_unlocked + else FishingShopStockType.get_price(item_id) + ), + ) + else: + _supplies_list.add_child(button) + if _shop_section == ShopSection.ART_SUPPLIES: + _add_stock_section("art kit") + var art_kit_grid := _add_stock_icon_grid() + _add_art_kit_button(art_kit_grid) + _add_stock_section("markers") + var marker_grid := _add_stock_icon_grid() + for product_id: StringName in ArtShopStockType.MARKER_PRODUCTS: + _add_art_upgrade_button(product_id, marker_grid) + _add_stock_section("brushes") + var brush_grid := _add_stock_icon_grid() + for product_id: StringName in ArtShopStockType.BRUSH_PRODUCTS: + _add_art_upgrade_button(product_id, brush_grid) + _add_stock_section("grids") + var canvas_grid := _add_stock_icon_grid() + for product_id: StringName in ArtShopStockType.GRID_PRODUCTS: + _add_art_upgrade_button(product_id, canvas_grid) + + +func _item_belongs_in_current_section(item: ItemDataType) -> bool: + match _shop_section: + ShopSection.BAIT: + return item.is_bait() or item.is_lure() + ShopSection.SNACKS: + return item.category == ItemDataType.Category.CONSUMABLE + ShopSection.EQUIPMENT: + return item.category == ItemDataType.Category.TOOL + return false func _add_stock_section(title: String) -> void: @@ -468,7 +797,21 @@ func _add_stock_section(title: String) -> void: _supplies_list.add_child(label) -func _add_art_kit_button() -> void: +func _add_stock_icon_grid() -> GridContainer: + var grid := GridContainer.new() + grid.columns = SUPPLY_ICON_GRID_COLUMNS + grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + grid.add_theme_constant_override( + "h_separation", SUPPLY_ICON_GRID_SEPARATION + ) + grid.add_theme_constant_override( + "v_separation", SUPPLY_ICON_GRID_SEPARATION + ) + _supplies_list.add_child(grid) + return grid + + +func _add_art_kit_button(parent: GridContainer) -> void: var item: ItemDataType = _item_catalog.get_item_by_id( ArtShopStockType.ART_KIT_ITEM_ID ) @@ -476,15 +819,22 @@ func _add_art_kit_button() -> void: return var owned: bool = _bag.get_quantity(ArtShopStockType.ART_KIT_ITEM_ID) > 0 var button: Button = _make_stock_button( - "%s\n$%d • %s" % [ + "%s\n%s" % [ item.display_name, - ArtShopStockType.ART_KIT_PRICE, "owned" if owned else "not owned", ], - item.description, + "%s\n%s\n%s" % [ + item.display_name, + "owned" if owned else "not owned", + item.description, + ], + ) + _configure_supply_icon_tile(button, item.icon) + _add_supply_icon_tile( + parent, + button, + ArtShopStockType.ART_KIT_PRICE, ) - button.icon = item.icon - button.expand_icon = true button.disabled = ( owned or _transaction_in_progress @@ -497,18 +847,39 @@ func _add_art_kit_button() -> void: button.pressed.connect(_purchase_art_kit) -func _add_art_upgrade_button(product_id: StringName) -> void: +func _add_art_upgrade_button( + product_id: StringName, + parent: GridContainer, +) -> void: var kit_owned: bool = ( _bag.get_quantity(ArtShopStockType.ART_KIT_ITEM_ID) > 0 ) var unlocked: bool = _art_unlocks.owns_product(product_id) var button: Button = _make_stock_button( - "%s\n$%d • %s" % [ + "%s\n%s" % [ ArtShopStockType.get_display_name(product_id), - ArtShopStockType.UPGRADE_PRICE, "unlocked" if unlocked else "locked", ], - ArtShopStockType.get_description(product_id), + "%s\n%s\n%s" % [ + ArtShopStockType.get_display_name(product_id), + "unlocked" if unlocked else "locked", + ArtShopStockType.get_description(product_id), + ], + ) + var marker_color_id: StringName = ( + PlayerArtUnlocksType.color_id_for_product(product_id) + ) + if marker_color_id.is_empty(): + _configure_supply_icon_tile(button, FALLBACK_SUPPLY_ICON) + else: + _configure_marker_icon_tile( + button, + SurfaceDrawingPalette.get_color(marker_color_id), + ) + _add_supply_icon_tile( + parent, + button, + ArtShopStockType.UPGRADE_PRICE, ) button.disabled = ( not kit_owned @@ -522,24 +893,143 @@ func _add_art_upgrade_button(product_id: StringName) -> void: button.pressed.connect(_purchase_art_upgrade.bind(product_id)) -func _make_stock_button(button_text: String, tooltip: String) -> Button: +func _make_stock_button( + button_text: String, + tooltip: String, +) -> Button: var button := Button.new() button.custom_minimum_size = Vector2(195, 54) button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.text = button_text button.tooltip_text = tooltip UtilityPageStyleType.apply_ocean_button(button) - _supplies_list.add_child(button) return button +func _configure_supply_icon_tile( + button: Button, + icon: Texture2D, +) -> void: + button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE + button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + button.icon = icon if icon != null else FALLBACK_SUPPLY_ICON + button.expand_icon = true + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.text = "" + + +func _configure_marker_icon_tile( + button: Button, + marker_color: Color, +) -> void: + button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE + button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + button.icon = null + button.text = "" + var icon := TextureRect.new() + icon.name = "MarkerIcon" + icon.position = Vector2(4.0, 4.0) + icon.size = Vector2(64.0, 64.0) + icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + icon.mouse_filter = Control.MOUSE_FILTER_IGNORE + var marker_material := ShaderMaterial.new() + marker_material.shader = ART_KIT_MARKER_SHADER + marker_material.set_shader_parameter("marker_color", marker_color) + icon.material = marker_material + icon.texture = ART_KIT_MARKER_ICON + button.add_child(icon) + + +func _add_supply_icon_tile( + parent: GridContainer, + button: Button, + price: int, +) -> void: + var tile_host := Control.new() + tile_host.custom_minimum_size = SUPPLY_ICON_HOST_SIZE + tile_host.mouse_filter = Control.MOUSE_FILTER_IGNORE + parent.add_child(tile_host) + button.position = Vector2.ZERO + button.size = SUPPLY_ICON_TILE_SIZE + tile_host.add_child(button) + var price_text := str(price) + var price_text_width: float = UtilityPageStyleType.TuffyFont.get_string_size( + price_text, + HORIZONTAL_ALIGNMENT_LEFT, + -1.0, + SUPPLY_PRICE_FONT_SIZE, + ).x + var price_width: float = minf( + SUPPLY_ICON_TILE_SIZE.x, + ceilf( + price_text_width + + SUPPLY_PRICE_ICON_SIZE + + SUPPLY_PRICE_ICON_GAP + + SUPPLY_PRICE_HORIZONTAL_PADDING + ), + ) + var price_bubble := PanelContainer.new() + price_bubble.name = "PriceBubble" + price_bubble.position = Vector2( + (SUPPLY_ICON_TILE_SIZE.x - price_width) * 0.5, + SUPPLY_PRICE_Y, + ) + price_bubble.size = Vector2(price_width, SUPPLY_PRICE_HEIGHT) + price_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE + price_bubble.z_index = 2 + var price_style := UtilityPageStyleType.rounded_style( + UtilityPageStyleType.OCEAN_FIELD, + 12, + ) + price_style.content_margin_left = 6.0 + price_style.content_margin_right = 6.0 + price_style.content_margin_top = 0.0 + price_style.content_margin_bottom = 0.0 + price_bubble.add_theme_stylebox_override("panel", price_style) + tile_host.add_child(price_bubble) + var price_row := HBoxContainer.new() + price_row.name = "CurrencyAmount" + price_row.mouse_filter = Control.MOUSE_FILTER_IGNORE + price_row.alignment = BoxContainer.ALIGNMENT_CENTER + price_row.add_theme_constant_override( + "separation", int(SUPPLY_PRICE_ICON_GAP) + ) + price_bubble.add_child(price_row) + var price_icon := TextureRect.new() + price_icon.name = "CurrencyIcon" + price_icon.custom_minimum_size = Vector2.ONE * SUPPLY_PRICE_ICON_SIZE + price_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE + price_icon.texture = CURRENCY_ICON + price_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + price_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + price_icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + price_row.add_child(price_icon) + var price_label := Label.new() + price_label.name = "Price" + price_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + price_label.text = price_text + price_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + price_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + price_label.add_theme_font_override( + "font", UtilityPageStyleType.TuffyFont + ) + price_label.add_theme_font_size_override( + "font_size", SUPPLY_PRICE_FONT_SIZE + ) + price_label.add_theme_color_override( + "font_color", SUPPLY_PRICE_COLOR + ) + price_label.add_theme_constant_override("outline_size", 0) + price_row.add_child(price_label) + + func _refresh_cooler_capacity() -> void: if _cooler_capacity == null: return var level: int = _cooler_capacity.get_level() var cost: int = _cooler_capacity.get_next_cost() var next_capacity: int = _cooler_capacity.get_next_capacity() - _cooler_level.text = "level %d" % level _cooler_purchase.disabled = ( cost < 0 or _transaction_in_progress @@ -548,18 +1038,25 @@ func _refresh_cooler_capacity() -> void: or not _network_shop.can_request_purchase() or not _cooler_capacity.can_purchase(_wallet) ) - _cooler_purchase.tooltip_text = _reel_purchase.tooltip_text + var cooler_effect: String if cost < 0: - _cooler_effect.text = "%d fish" % _cooler_capacity.get_capacity() - _cooler_cost.text = "max" - _cooler_purchase.text = "max" + cooler_effect = "%d fish · maximum level" % ( + _cooler_capacity.get_capacity() + ) + _cooler_price_bubble.hide() else: - _cooler_effect.text = "%d → %d fish" % [ + _cooler_price_bubble.show() + cooler_effect = "%d → %d fish" % [ _cooler_capacity.get_capacity(), next_capacity, ] - _cooler_cost.text = "$%d" % cost - _cooler_purchase.text = "purchase" + _cooler_cost.set_amount(cost) + _cooler_purchase.tooltip_text = _upgrade_tooltip( + "cooler capacity", + level, + cooler_effect, + ) + _cooler_purchase.accessibility_name = _cooler_purchase.tooltip_text func _purchase_reel_speed() -> void: @@ -572,54 +1069,65 @@ func _purchase_barrier_power() -> void: func _purchase_supply(item_id: StringName) -> void: if _transaction_in_progress or not _is_transaction_context_valid(): - _feedback.text = "unable to complete purchase." + _set_feedback("unable to complete purchase.") return - if not _bag.can_add_item(item_id, 1): - _feedback.text = "Your Bag is full." + var owned: int = _bag.get_quantity(item_id) + var item: ItemDataType = _item_catalog.get_item_by_id(item_id) + if item == null: + _set_feedback("Purchase could not be completed.") return - if not _wallet.can_afford(FishingShopStockType.get_price(item_id)): - _feedback.text = "Not enough fish coin." + var quantity: int = FishingShopStockType.get_purchase_quantity( + item_id, owned, item.max_stack + ) + if quantity <= 0 or not _bag.can_add_item(item_id, quantity): + _set_feedback("Your Bag is full.") + return + var total_cost: int = FishingShopStockType.get_purchase_cost( + item_id, quantity, _bag.is_bait_unlocked(item_id) + ) + if total_cost < 0 or not _wallet.can_afford(total_cost): + _set_feedback("Insufficient funds.") return if _network_shop == null: - _feedback.text = "Purchase could not be completed." + _set_feedback("Purchase could not be completed.") return _network_shop.request_supply(item_id) func _purchase_art_kit() -> void: if _network_shop == null or not _is_transaction_context_valid(): - _feedback.text = "Purchase could not be completed." + _set_feedback("Purchase could not be completed.") return _network_shop.request_art_kit() func _purchase_art_upgrade(product_id: StringName) -> void: if _network_shop == null or not _is_transaction_context_valid(): - _feedback.text = "Purchase could not be completed." + _set_feedback("Purchase could not be completed.") return _network_shop.request_art_upgrade(product_id) func _purchase_cooler_capacity() -> void: if _transaction_in_progress or not _is_transaction_context_valid(): - _feedback.text = "unable to complete purchase." + _set_feedback("unable to complete purchase.") return var cost: int = _cooler_capacity.get_next_cost() if cost < 0: - _feedback.text = "Upgrade is already at maximum." + _set_feedback("Upgrade is already at maximum.") return if not _wallet.can_afford(cost): - _feedback.text = "Not enough fish coin." + _set_feedback("Insufficient funds.") return if _network_shop == null: - _feedback.text = "Purchase could not be completed." + _set_feedback("Purchase could not be completed.") return _network_shop.request_cooler_capacity_upgrade() func _purchase_upgrade(is_reel_speed: bool) -> void: if _transaction_in_progress or not _is_transaction_context_valid(): - _feedback.text = "unable to complete purchase." + _set_feedback("unable to complete purchase.") return var cost: int = ( _upgrades.get_next_reel_speed_cost() @@ -627,13 +1135,13 @@ func _purchase_upgrade(is_reel_speed: bool) -> void: else _upgrades.get_next_barrier_power_cost() ) if cost < 0: - _feedback.text = "Upgrade is already at maximum." + _set_feedback("Upgrade is already at maximum.") return if not _wallet.can_afford(cost): - _feedback.text = "Not enough fish coin." + _set_feedback("Insufficient funds.") return if _network_shop == null: - _feedback.text = "Purchase could not be completed." + _set_feedback("Purchase could not be completed.") return if is_reel_speed: _network_shop.request_reel_speed_upgrade() @@ -644,7 +1152,7 @@ func _purchase_upgrade(is_reel_speed: bool) -> void: func _on_network_purchase_pending(_request_id: String) -> void: _transaction_in_progress = true if visible: - _feedback.text = "Purchasing…" + _set_feedback("Purchasing…") _refresh_all() @@ -660,14 +1168,21 @@ func _on_network_purchase_finished( _transaction_in_progress = false if not visible: return - _feedback.text = ( - "Purchase complete. $%d spent." % total_cost + _set_feedback( + ( + "Purchase complete • %s spent." + % CurrencyPresentation.bbcode_amount(total_cost, 18) + ) if accepted else message ) _refresh_all() +func _set_feedback(message: String) -> void: + _feedback.text = "[center]%s[/center]" % message + + func _is_transaction_context_valid() -> bool: return ( visible diff --git a/ui/fishing_shop.tscn b/ui/fishing_shop.tscn index 2b1914d..5d0b9ec 100644 --- a/ui/fishing_shop.tscn +++ b/ui/fishing_shop.tscn @@ -1,11 +1,13 @@ -[gd_scene load_steps=7 format=3] +[gd_scene load_steps=9 format=3] [ext_resource type="Script" path="res://ui/fishing_shop.gd" id="1_script"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] -[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"] -[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"] -[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"] -[ext_resource type="Texture2D" path="res://items/icons/placeholder/cooler_expansion.svg" id="6_cooler"] +[ext_resource type="Texture2D" path="res://items/icons/shop/64_speed_plus.png" id="3_reel"] +[ext_resource type="Texture2D" path="res://items/icons/shop/64_power_plus.png" id="4_barrier"] +[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="5_coin"] +[ext_resource type="Texture2D" path="res://items/icons/equipment/64_cooler_plus.png" id="6_cooler"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="7_close"] +[ext_resource type="PackedScene" path="res://ui/components/currency_amount.tscn" id="8_currency"] [node name="FishingShop" type="Control"] unique_name_in_owner = true @@ -34,7 +36,7 @@ mouse_filter = 0 [node name="ShopCoolerPage" type="Control" parent="."] unique_name_in_owner = true visible = false -z_index = 90 +z_index = 102 layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -53,17 +55,8 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 1 -[node name="BackToShopButton" type="Button" parent="ShopCoolerPage"] -unique_name_in_owner = true -z_index = 110 -layout_mode = 0 -offset_left = 1052.0 -offset_top = 54.0 -offset_right = 1218.0 -offset_bottom = 104.0 -text = "back to shop" - [node name="ShopPanel" type="PanelContainer" parent="."] +z_index = 101 unique_name_in_owner = true layout_mode = 1 anchors_preset = 8 @@ -100,55 +93,68 @@ text = "fishing shop" theme_override_font_sizes/font_size = 32 theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) -[node name="CoinIcon" type="TextureRect" parent="ShopPanel/Margin/Layout/Header"] -custom_minimum_size = Vector2(24, 24) +[node name="BalanceDisplay" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Header"] +custom_minimum_size = Vector2(0, 48) layout_mode = 2 +size_flags_vertical = 4 +theme_override_constants/separation = 6 +alignment = 1 + +[node name="CoinIcon" type="TextureRect" parent="ShopPanel/Margin/Layout/Header/BalanceDisplay"] +custom_minimum_size = Vector2(26, 26) +layout_mode = 2 +size_flags_vertical = 4 texture = ExtResource("5_coin") expand_mode = 1 stretch_mode = 5 texture_filter = 1 -[node name="WalletLabel" type="Label" parent="ShopPanel/Margin/Layout/Header"] +[node name="WalletLabel" type="Label" parent="ShopPanel/Margin/Layout/Header/BalanceDisplay"] unique_name_in_owner = true +custom_minimum_size = Vector2(0, 32) layout_mode = 2 -text = "wallet: $0" +size_flags_vertical = 4 +text = "0" +vertical_alignment = 1 [node name="CloseButton" type="Button" parent="ShopPanel/Margin/Layout/Header"] unique_name_in_owner = true +custom_minimum_size = Vector2(48, 48) layout_mode = 2 -text = "close" -custom_minimum_size = Vector2(104, 48) +tooltip_text = "Close shop" +flat = true +icon = ExtResource("7_close") +expand_icon = true +icon_max_width = 40 -[node name="ModeTabs" type="HBoxContainer" parent="ShopPanel/Margin/Layout"] -layout_mode = 2 -alignment = 1 -theme_override_constants/separation = 10 - -[node name="BuyModeButton" type="Button" parent="ShopPanel/Margin/Layout/ModeTabs"] -unique_name_in_owner = true -custom_minimum_size = Vector2(180, 42) -layout_mode = 2 -text = "buy gear & supplies" - -[node name="SellModeButton" type="Button" parent="ShopPanel/Margin/Layout/ModeTabs"] -unique_name_in_owner = true -custom_minimum_size = Vector2(180, 42) -layout_mode = 2 -text = "sell fish" - -[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"] +[node name="Feedback" type="RichTextLabel" parent="ShopPanel/Margin/Layout"] unique_name_in_owner = true +custom_minimum_size = Vector2(0, 28) layout_mode = 2 text = "" -horizontal_alignment = 1 -theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) +bbcode_enabled = true +fit_content = true +scroll_active = false +theme_override_colors/default_color = Color(1, 0.82, 0.4, 1) +theme_override_font_sizes/normal_font_size = 20 + +[node name="ShopTabBar" type="HBoxContainer" parent="."] +unique_name_in_owner = true +z_index = 100 +offset_left = 180.0 +offset_top = 26.0 +offset_right = 1100.0 +offset_bottom = 70.0 +alignment = 1 +theme_override_constants/separation = 6 [node name="Body" type="HBoxContainer" parent="ShopPanel/Margin/Layout"] layout_mode = 2 size_flags_vertical = 3 -theme_override_constants/separation = 12 +theme_override_constants/separation = 0 [node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"] +unique_name_in_owner = true custom_minimum_size = Vector2(270, 0) layout_mode = 2 size_flags_horizontal = 3 @@ -160,12 +166,14 @@ text = "fishing upgrades" theme_override_font_sizes/font_size = 23 [node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"] +unique_name_in_owner = true custom_minimum_size = Vector2(210, 0) layout_mode = 2 size_flags_horizontal = 3 theme_override_constants/separation = 6 -[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"] +[node name="StockTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"] +unique_name_in_owner = true layout_mode = 2 text = "shop stock" theme_override_font_sizes/font_size = 23 @@ -181,164 +189,102 @@ layout_mode = 2 size_flags_horizontal = 3 theme_override_constants/separation = 5 -[node name="ReelCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"] +[node name="UpgradeScroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"] layout_mode = 2 +size_flags_vertical = 3 +horizontal_scroll_mode = 0 -[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard"] -layout_mode = 2 -theme_override_constants/margin_left = 9 -theme_override_constants/margin_top = 7 -theme_override_constants/margin_right = 9 -theme_override_constants/margin_bottom = 7 - -[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin"] -layout_mode = 2 -theme_override_constants/separation = 9 - -[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"] -custom_minimum_size = Vector2(44, 44) -layout_mode = 2 -texture = ExtResource("3_reel") -expand_mode = 1 -stretch_mode = 5 -texture_filter = 1 - -[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"] +[node name="UpgradeGrid" type="GridContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll"] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 +theme_override_constants/h_separation = 20 +theme_override_constants/v_separation = 20 +columns = 3 -[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] +[node name="ReelTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"] +custom_minimum_size = Vector2(144, 168) layout_mode = 2 -text = "reel speed" -theme_override_font_sizes/font_size = 22 +mouse_filter = 2 -[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] +[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile"] +unique_name_in_owner = true +offset_right = 144.0 +offset_bottom = 144.0 +tooltip_text = "reel speed" +icon = ExtResource("3_reel") +expand_icon = true +icon_max_width = 128 + +[node name="ReelPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile"] +unique_name_in_owner = true +z_index = 2 +offset_left = 22.0 +offset_top = 128.0 +offset_right = 122.0 +offset_bottom = 160.0 +mouse_filter = 2 + +[node name="ReelCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile/ReelPriceBubble" instance=ExtResource("8_currency")] unique_name_in_owner = true layout_mode = 2 -text = "level 0" -theme_override_font_sizes/font_size = 18 +amount = 50 +icon_size = 24.0 -[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] +[node name="BarrierTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"] +custom_minimum_size = Vector2(144, 168) +layout_mode = 2 +mouse_filter = 2 + +[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile"] +unique_name_in_owner = true +offset_right = 144.0 +offset_bottom = 144.0 +tooltip_text = "rod power" +icon = ExtResource("4_barrier") +expand_icon = true +icon_max_width = 128 + +[node name="BarrierPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile"] +unique_name_in_owner = true +z_index = 2 +offset_left = 22.0 +offset_top = 128.0 +offset_right = 122.0 +offset_bottom = 160.0 +mouse_filter = 2 + +[node name="BarrierCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile/BarrierPriceBubble" instance=ExtResource("8_currency")] unique_name_in_owner = true layout_mode = 2 -text = "1.00× → 1.10×" -theme_override_font_sizes/font_size = 18 +amount = 100 +icon_size = 24.0 -[node name="ReelCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"] +[node name="CoolerTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"] +custom_minimum_size = Vector2(144, 168) +layout_mode = 2 +mouse_filter = 2 + +[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile"] +unique_name_in_owner = true +offset_right = 144.0 +offset_bottom = 144.0 +tooltip_text = "cooler capacity" +icon = ExtResource("6_cooler") +expand_icon = true +icon_max_width = 128 + +[node name="CoolerPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile"] +unique_name_in_owner = true +z_index = 2 +offset_left = 22.0 +offset_top = 128.0 +offset_right = 122.0 +offset_bottom = 160.0 +mouse_filter = 2 + +[node name="CoolerCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile/CoolerPriceBubble" instance=ExtResource("8_currency")] unique_name_in_owner = true layout_mode = 2 -text = "$50" -theme_override_font_sizes/font_size = 18 - -[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"] -unique_name_in_owner = true -layout_mode = 2 -text = "purchase" - -[node name="BarrierCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"] -layout_mode = 2 - -[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard"] -layout_mode = 2 -theme_override_constants/margin_left = 9 -theme_override_constants/margin_top = 7 -theme_override_constants/margin_right = 9 -theme_override_constants/margin_bottom = 7 - -[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin"] -layout_mode = 2 -theme_override_constants/separation = 9 - -[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"] -custom_minimum_size = Vector2(44, 44) -layout_mode = 2 -texture = ExtResource("4_barrier") -expand_mode = 1 -stretch_mode = 5 -texture_filter = 1 - -[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] -layout_mode = 2 -text = "barrier power" -theme_override_font_sizes/font_size = 22 - -[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "level 0" -theme_override_font_sizes/font_size = 18 - -[node name="BarrierEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "1 damage → 2 damage" -theme_override_font_sizes/font_size = 18 - -[node name="BarrierCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "$100" -theme_override_font_sizes/font_size = 18 - -[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"] -unique_name_in_owner = true -layout_mode = 2 -text = "purchase" - -[node name="CoolerCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"] -layout_mode = 2 - -[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard"] -layout_mode = 2 -theme_override_constants/margin_left = 9 -theme_override_constants/margin_top = 7 -theme_override_constants/margin_right = 9 -theme_override_constants/margin_bottom = 7 - -[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin"] -layout_mode = 2 -theme_override_constants/separation = 9 - -[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"] -custom_minimum_size = Vector2(44, 44) -layout_mode = 2 -texture = ExtResource("6_cooler") -expand_mode = 1 -stretch_mode = 5 -texture_filter = 1 - -[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] -layout_mode = 2 -text = "cooler capacity" -theme_override_font_sizes/font_size = 22 - -[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "level 0" -theme_override_font_sizes/font_size = 18 - -[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "12 → 18 fish" -theme_override_font_sizes/font_size = 18 - -[node name="CoolerCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"] -unique_name_in_owner = true -layout_mode = 2 -text = "$75" -theme_override_font_sizes/font_size = 18 - -[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"] -unique_name_in_owner = true -layout_mode = 2 -text = "purchase" +amount = 75 +icon_size = 24.0 diff --git a/ui/fonts/Seattle-Avenue-LICENSE.txt b/ui/fonts/Seattle-Avenue-LICENSE.txt new file mode 100644 index 0000000..22cdbca --- /dev/null +++ b/ui/fonts/Seattle-Avenue-LICENSE.txt @@ -0,0 +1,23 @@ +Seattle Avenue Regular +Creator: JLH Fonts +Status: Public domain — dedicated by the author/creator + +Source record retrieved 2026-08-12: +https://www.publicdomainfiles.com/show_file.php?id=13486266913972 + +The source page states that the file was dedicated to the public domain by the +author/creator and may be freely reproduced, distributed, transmitted, used, +modified, built upon, or otherwise exploited for commercial or noncommercial +purposes. + +Additional longstanding attribution and public-domain listing: +https://www.dafont.com/seattle-avenue.font + +Repository font file: +ui/fonts/seattle_avenue.otf + +Repository font SHA-256: +ee288a4a3c67a5339141bc08b14e7da84b7657d73ec1655c03869285c8af6324 + +This record documents the published public-domain dedication and creator +attribution. It does not impose additional restrictions on the font. diff --git a/ui/game_theme.tres b/ui/game_theme.tres index 7da4650..e21f90b 100644 --- a/ui/game_theme.tres +++ b/ui/game_theme.tres @@ -144,6 +144,17 @@ corner_radius_top_right = 10 corner_radius_bottom_right = 10 corner_radius_bottom_left = 10 +[sub_resource type="StyleBoxFlat" id="TooltipPanel"] +content_margin_left = 12.0 +content_margin_top = 8.0 +content_margin_right = 12.0 +content_margin_bottom = 8.0 +bg_color = Color(0.031, 0.122, 0.169, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + [resource] default_font = ExtResource("1_font") default_font_size = 23 @@ -175,3 +186,6 @@ OptionButton/styles/hover = SubResource("ButtonHover") OptionButton/styles/normal = SubResource("ButtonNormal") OptionButton/styles/pressed = SubResource("ButtonPressed") PanelContainer/styles/panel = SubResource("Panel") +TooltipLabel/colors/font_color = Color(0.765, 0.875, 0.902, 1) +TooltipLabel/font_sizes/font_size = 18 +TooltipPanel/styles/panel = SubResource("TooltipPanel") diff --git a/ui/game_ui.gd b/ui/game_ui.gd index 0bcaa53..27db6e8 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -12,6 +12,7 @@ const PlayerMenuType = preload("res://ui/player_menu.gd") const PlayerType = preload("res://player/player.gd") const PlayerWalletType = preload("res://economy/player_wallet.gd") const ItemCatalogType = preload("res://items/item_catalog.gd") +const ItemDataType = preload("res://items/item_data.gd") const PlayerBagType = preload("res://inventory/player_bag.gd") const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") const HotbarUIType = preload("res://ui/hotbar.gd") @@ -34,12 +35,19 @@ const PlayerCoolerCapacityType = preload( ) const ChatUIType = preload("res://ui/chat_ui.gd") const EmoteRadialMenuType = preload("res://ui/emote_radial_menu.gd") +const QuickRadialMenuType = preload("res://ui/quick_radial_menu.gd") +const ControllerVirtualCursorType = preload( + "res://ui/controller_virtual_cursor.gd" +) const SurfaceDrawingToolbarType = preload( "res://ui/surface_drawing_toolbar.gd" ) const PlayerSettingsManagerType = preload( "res://settings/player_settings_manager.gd" ) +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) const WorldTimeServiceType = preload("res://world/world_time_service.gd") const WorldWeatherServiceType = preload( "res://world/world_weather_service.gd" @@ -50,6 +58,12 @@ const PlayerExperienceType = preload( const UIReferencePresentationType = preload( "res://ui/ui_reference_presentation.gd" ) +const MAIN_BUBBLE_PROFILE: BubbleMenuProfile = preload( + "res://ui/components/bubble_menu/bubble_menu_profile.tres" +) +const ACTIVE_BAIT_EMPTY_ICON: Texture2D = preload( + "res://ui/icons/pictograms/x_dark.png" +) signal pixelation_settings_visibility_changed(is_visible: bool) signal crisp_reset_focus_requested @@ -58,8 +72,32 @@ signal passive_pointer_ui_changed(is_enabled: bool) signal player_menu_backdrop_visibility_changed(is_visible: bool) signal shop_backdrop_visibility_changed(is_visible: bool) +const VIRTUAL_MOUSE_INPUT_OWNER: StringName = &"controller_virtual_mouse" +const VIRTUAL_MOUSE_TRIGGER_THRESHOLD: float = 0.55 +const VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD: float = 0.35 +const VIRTUAL_MOUSE_STICK_DEADZONE: float = 0.18 +const VIRTUAL_MOUSE_SPEED: float = 720.0 +const CONTROLLER_MENU_SCROLL_DEADZONE: float = 0.25 +const CONTROLLER_MENU_SCROLL_SPEED: float = 660.0 +# Android controller mappings may expose LT on its own axis or as the negative +# half of the same signed axis used by RT. Support both without letting the RT +# zoom direction enter virtual-pointer mode. +const VIRTUAL_MOUSE_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_RIGHT +const VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT +const VIRTUAL_MOUSE_SECONDARY_CLICK_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT +const FISHING_PANEL_DEFAULT_TOP_OFFSET: float = -210.0 +const FISHING_PANEL_DEFAULT_BOTTOM_OFFSET: float = -140.0 +const FISHING_PANEL_SHOWCASE_TOP_OFFSET: float = -174.0 +const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0 + @onready var _status_label: Label = %StatusLabel +@onready var _bite_prompt_button: Button = %BitePromptButton @onready var _gameplay_transient_hud: Control = %GameplayTransientHUD +@onready var _active_bait_indicator: Control = %ActiveBaitIndicator +@onready var _active_bait_button: Button = %ActiveBaitButton +@onready var _active_bait_quantity_badge: Panel = %ActiveBaitQuantityBadge +@onready var _active_bait_quantity: Label = %ActiveBaitQuantity +@onready var _experience_presentation: Control = %ExperiencePresentation @onready var _catch_track: Control = %CatchTrack @onready var _green_catch_progress: ProgressBar = %GreenCatchProgress @onready var _red_chase_progress: ProgressBar = %RedChaseProgress @@ -75,6 +113,18 @@ signal shop_backdrop_visibility_changed(is_visible: bool) @onready var _experience_progress: ProgressBar = %ExperienceProgress @onready var _experience_bubble: PanelContainer = %ExperienceBubble @onready var _experience_bubble_label: Label = %ExperienceBubbleLabel +const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd") +const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd") +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) +const SHOP_ANIMALESE_VOICE_ID: String = "natural" +const SHOP_ANIMALESE_BASE_PITCH: float = 1.08 +const SHOP_SPEECH_CHARACTERS_PER_SECOND: float = 28.0 +const SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS: int = 5000 +const ART_KIT_ITEM_ID: StringName = &"art_kit" + + @onready var _canonical_stage: Control = %CanonicalStage @onready var _player_menu: PlayerMenuType = %PlayerMenu @onready var _screen_fade: ScreenFade = %ScreenFade @@ -82,10 +132,23 @@ signal shop_backdrop_visibility_changed(is_visible: bool) @onready var _pause_menu: PauseMenuType = %PauseMenu @onready var _hotbar_ui: HotbarUIType = %Hotbar @onready var _fishing_shop: FishingShopType = %FishingShop -@onready var _shop_prompt: PanelContainer = %ShopPrompt +@onready var _shop_prompt: Control = %ShopPrompt +@onready var _shop_prompt_bubble: PanelContainer = %ShopPromptBubble +@onready var _shop_prompt_message: Label = %ShopPromptMessage +@onready var _shop_prompt_key_badge: Panel = %ShopPromptKeyBadge +@onready var _shop_prompt_key: Label = %ShopPromptKey +@onready var _shop_prompt_pointer: Polygon2D = %ShopPromptPointer +var _shop_animalese_voice: AnimaleseVoiceType +var _shop_npc_player_in_range: bool = false +var _shop_npc_spoken_for_current_visit: bool = false +var _shop_npc_next_speech_msec: int = 0 @onready var _effect_status: Label = %EffectStatus @onready var _chat_ui: ChatUIType = %ChatUI @onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu +@onready var _quick_radial_menu: QuickRadialMenuType = %QuickRadialMenu +@onready var _controller_virtual_cursor: ControllerVirtualCursorType = ( + %ControllerVirtualCursor +) @onready var _surface_drawing_toolbar: SurfaceDrawingToolbarType = ( %SurfaceDrawingToolbar ) @@ -98,8 +161,15 @@ signal shop_backdrop_visibility_changed(is_visible: bool) var _showcase_active: bool = false var _player: PlayerType +var _network_chat_service: NetworkChatService +var _network_profile: NetworkProfilePreferences +var _spawn_service: PlayerSpawnService +var _bag: PlayerBagType +var _hotbar: PlayerHotbarType +var _item_catalog: ItemCatalogType var _player_menu_open: bool = false var _gameplay_ui_enabled: bool = false +var _gameplay_hud_hidden: bool = false var _fishing_spot: FishingSpotType var _system_menu_open: bool = false var _shop_open: bool = false @@ -109,21 +179,62 @@ var _item_effects: PlayerItemEffectsType var _main_shop_buyer: FishBuyerProfileType var _shop_interaction: ShopInteractionType var _surface_drawing: NetworkSurfaceDrawingService +var _surface_drawing_hotbar_selected: bool = false var _experience: PlayerExperienceType var _experience_award_queue: Array[Dictionary] = [] var _experience_animation_active: bool = false var _experience_animation_generation: int = 0 var _experience_panel_rest_y: float = 18.0 +var _emote_prior_camera_input_enabled: bool = true +var _quick_prior_camera_input_enabled: bool = true +var _virtual_mouse_active: bool = false +var _virtual_mouse_prior_camera_input_enabled: bool = true +var _virtual_mouse_prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE +var _virtual_mouse_window_position: Vector2 = Vector2.ZERO +var _virtual_mouse_button_mask: int = 0 +var _virtual_mouse_right_pressed: bool = false +var _virtual_mouse_device_id: int = 0 +var _virtual_mouse_trigger_strength: float = 0.0 +var _virtual_mouse_stick: Vector2 = Vector2.ZERO +var _virtual_mouse_trigger_rest_by_device: Dictionary[int, float] = {} +var _shared_trigger_rest_by_device: Dictionary[int, float] = {} +var _controller_mapping_manager: ControllerMappingManagerType +var _settings_manager: PlayerSettingsManagerType func _ready() -> void: + _bite_prompt_button.pressed.connect(_on_bite_prompt_pressed) + _apply_active_bait_indicator_style() + _refresh_active_bait_indicator() + # Reward feedback must remain above full-screen canonical menus. Keeping the + # overlay as the final stage child makes that ownership explicit instead of + # relying on scene declaration order when another menu adds high-z children. + var presentation_parent: Node = _experience_presentation.get_parent() + if presentation_parent != null: + presentation_parent.move_child( + _experience_presentation, + presentation_parent.get_child_count() - 1, + ) _emote_radial_menu.emote_selected.connect(_on_emote_selected) + _quick_radial_menu.action_selected.connect(_on_quick_action_selected) _chat_ui.text_entry_ownership_changed.connect( _on_chat_text_entry_ownership_changed ) _hotbar_ui.presentation_transition_finished.connect( _on_hotbar_presentation_transition_finished ) + _player_menu.controller_hotbar_placement_requested.connect( + _on_controller_hotbar_placement_requested + ) + _player_menu.controller_hotbar_placement_ended.connect( + _on_controller_hotbar_placement_ended + ) + _player_menu.controller_hotbar_management_requested.connect( + _on_controller_hotbar_management_requested + ) + _player_menu.controller_hotbar_management_ended.connect( + _on_controller_hotbar_management_ended + ) _title_settings_panel.panel_visibility_changed.connect( _on_settings_visibility_changed ) @@ -136,6 +247,10 @@ func _ready() -> void: _pause_settings_panel.crisp_reset_focus_requested.connect( crisp_reset_focus_requested.emit ) + if not Input.joy_connection_changed.is_connected( + _on_controller_connection_changed + ): + Input.joy_connection_changed.connect(_on_controller_connection_changed) func setup( @@ -166,16 +281,51 @@ func setup( reservations: PlayerAssetReservationService, network_profile_service: NetworkProfileService, network_player_list: NetworkPlayerListService, + discovery: DiscoveryClient, settings_manager: PlayerSettingsManagerType, surface_drawing: NetworkSurfaceDrawingService, art_unlocks: PlayerArtUnlocks, world_time: WorldTimeServiceType, world_weather: WorldWeatherServiceType, + player_jobs: PlayerJobService, + world_environment: WorldEnvironment, + world_sun: DirectionalLight3D, ) -> void: _player = player + _network_chat_service = network_chat_service + _network_profile = network_profile + _spawn_service = spawn_service + _bag = bag + _hotbar = hotbar + _item_catalog = item_catalog + _settings_manager = settings_manager _fishing_spot = fishing_spot _item_effects = item_effects _experience = experience + if ( + _player != null + and not _player.active_bait_changed.is_connected( + _on_hud_active_bait_changed + ) + ): + _player.active_bait_changed.connect(_on_hud_active_bait_changed) + if ( + _bag != null + and not _bag.contents_changed.is_connected( + _on_hud_bait_inventory_changed + ) + ): + _bag.contents_changed.connect(_on_hud_bait_inventory_changed) + _refresh_active_bait_indicator() + if ( + _network_chat_service != null + and not _network_chat_service.character_call_received.is_connected( + _on_character_call_received + ) + ): + _network_chat_service.character_call_received.connect( + _on_character_call_received + ) if ( _experience != null and not _experience.experience_awarded.is_connected( @@ -185,7 +335,7 @@ func setup( _experience.experience_awarded.connect(_on_experience_awarded) _chat_ui.setup( network_chat_service, network_session, spawn_service, player, - fishing_spot, settings_manager, world_time, world_weather + fishing_spot, settings_manager, world_time, world_weather, item_effects ) _title_settings_panel.setup_network_profile( network_profile, network_session @@ -194,9 +344,9 @@ func setup( network_profile, network_session ) fishing_spot.status_changed.connect(_on_fishing_status_changed) + fishing_spot.bite_prompt_changed.connect(_on_bite_prompt_changed) fishing_spot.catch_display_changed.connect(_on_catch_display_changed) fishing_spot.showcase_changed.connect(_on_showcase_changed) - fishing_spot.art_ui_toggle_requested.connect(_toggle_surface_drawing) _player_menu.menu_visibility_changed.connect( _on_player_menu_visibility_changed ) @@ -211,6 +361,7 @@ func setup( player, inventory, collection_log, + experience, wallet, sale_service, default_buyer, @@ -226,7 +377,21 @@ func setup( reservations, network_profile_service, network_player_list, + discovery, + player_jobs, + world_time, + world_environment, + world_sun, ) + if ( + _settings_manager != null + and not _settings_manager.settings_changed.is_connected( + _on_player_settings_changed + ) + ): + _settings_manager.settings_changed.connect(_on_player_settings_changed) + if _settings_manager != null: + _on_player_settings_changed(_settings_manager.current_settings) _hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot, inventory) _fishing_shop.setup( player, @@ -242,6 +407,7 @@ func setup( network_shop_service, ) _fishing_shop.menu_visibility_changed.connect(_on_shop_visibility_changed) + _fishing_shop.menu_exit_started.connect(_on_shop_exit_started) _fishing_shop.sell_fish_requested.connect(_on_shop_sell_fish_requested) _fishing_shop.shop_cooler_return_requested.connect( _on_shop_cooler_return_requested @@ -256,32 +422,48 @@ func setup( set_edge_docks( settings_manager.current_settings.chat_dock_right, settings_manager.current_settings.paint_dock_right, + settings_manager.current_settings.chat_mobile_mode, ) func _input(event: InputEvent) -> void: + if _handle_virtual_mouse_input(event): + get_viewport().set_input_as_handled() + return + if _handle_controller_chat_controls(event): + get_viewport().set_input_as_handled() + return + if ( + event.is_action_pressed("toggle_hud") + and not (event is InputEventKey and event.echo) + and _can_toggle_gameplay_hud() + ): + set_gameplay_hud_hidden(not _gameplay_hud_hidden) + get_viewport().set_input_as_handled() + return if ( _surface_drawing_toolbar != null and _surface_drawing_toolbar.owns_pointer_event(event) ): return - var drawing_can_open: bool = ( - _gameplay_ui_enabled - and not _system_menu_open - and not _player_menu_open - and not _shop_open - and not _chat_input_open - and not _showcase_active - and _fishing_spot != null - and _fishing_spot.can_use_surface_drawing() - ) + if event.is_action_pressed("character_call") and _can_use_character_call(): + _network_chat_service.send_local_character_call( + _network_profile.call_id + ) + get_viewport().set_input_as_handled() + return + var drawing_can_open: bool = _can_surface_drawing_be_active() if ( _surface_drawing != null - and _surface_drawing.handle_input(event, drawing_can_open) + and _surface_drawing.handle_input( + event, + drawing_can_open, + _drawing_pointer_window_position(), + ) ): get_viewport().set_input_as_handled() return - if _emote_radial_menu == null: + if _emote_radial_menu == null or _quick_radial_menu == null: return var can_open: bool = ( _gameplay_ui_enabled @@ -290,9 +472,137 @@ func _input(event: InputEvent) -> void: and not _shop_open and not _chat_input_open and not _showcase_active + and not _virtual_mouse_active ) - if _emote_radial_menu.handle_input(event, can_open): + var emote_was_open: bool = _emote_radial_menu.is_open() + if _emote_radial_menu.handle_input( + event, + can_open and not _quick_radial_menu.is_open(), + ): + var emote_is_open: bool = _emote_radial_menu.is_open() + if emote_is_open != emote_was_open and _player != null: + if emote_is_open: + _emote_prior_camera_input_enabled = ( + _player.is_camera_input_enabled() + ) + _player.set_camera_input_enabled(false) + else: + _player.set_camera_input_enabled( + _emote_prior_camera_input_enabled + ) get_viewport().set_input_as_handled() + return + var quick_was_open: bool = _quick_radial_menu.is_open() + if _quick_radial_menu.handle_input( + event, + can_open and not _emote_radial_menu.is_open(), + ): + var quick_is_open: bool = _quick_radial_menu.is_open() + if quick_is_open != quick_was_open and _player != null: + if quick_is_open: + _quick_prior_camera_input_enabled = ( + _player.is_camera_input_enabled() + ) + _player.set_camera_input_enabled(false) + else: + _player.set_camera_input_enabled( + _quick_prior_camera_input_enabled + ) + get_viewport().set_input_as_handled() + + +func _can_use_character_call() -> bool: + return ( + _gameplay_ui_enabled + and not _system_menu_open + and not _player_menu_open + and not _shop_open + and not _chat_input_open + and not _showcase_active + and not _virtual_mouse_active + and _network_chat_service != null + and _network_profile != null + ) + + +func _on_character_call_received( + peer_id: int, + call_id: String, + pitch_scale: float, +) -> void: + if _spawn_service == null: + return + var avatar: PlayerType = _spawn_service.get_avatar(peer_id) + if avatar == null: + return + avatar.play_character_call_visual() + var audio_path: String = VoiceProfilesType.call_audio_path(call_id) + if not ResourceLoader.exists(audio_path, "AudioStream"): + return + var stream: AudioStream = load(audio_path) as AudioStream + if stream == null: + return + var call_player := AudioStreamPlayer3D.new() + call_player.name = "CharacterCall" + call_player.bus = &"SFX" + call_player.stream = stream + call_player.pitch_scale = pitch_scale + call_player.max_distance = 28.0 + call_player.unit_size = 4.0 + call_player.position = ( + Vector3.UP * 1.25 * avatar.get_character_visual_scale() + ) + avatar.add_child(call_player) + call_player.finished.connect(call_player.queue_free) + call_player.play() + + +func _handle_controller_chat_controls(event: InputEvent) -> bool: + var button_event: InputEventJoypadButton = event as InputEventJoypadButton + if ( + button_event == null + or not button_event.pressed + or not _gameplay_ui_enabled + or _system_menu_open + or _player_menu_open + or _shop_open + ): + return false + var use_mapping: bool = ( + _controller_mapping_manager != null + ) + var select_pressed: bool = ( + _controller_mapping_manager.event_matches_role( + event, ControllerMappingManagerType.ROLE_SELECT + ) + if use_mapping + else button_event.button_index == JOY_BUTTON_BACK + ) + var focus_pressed: bool = ( + _controller_mapping_manager.event_matches_role( + event, ControllerMappingManagerType.ROLE_LB + ) + if use_mapping + else button_event.button_index == JOY_BUTTON_LEFT_SHOULDER + ) + var accept_pressed: bool = ( + _controller_mapping_manager.event_matches_role( + event, ControllerMappingManagerType.ROLE_A + ) + if use_mapping + else button_event.button_index == JOY_BUTTON_A + ) + if select_pressed: + # Handle Select before focused LineEdit controls consume it. Select owns + # chat visibility while LB only returns input ownership to the world. + _chat_ui.toggle_chat() + return true + if focus_pressed: + _chat_ui.refocus_gameplay() + return true + if accept_pressed: + return _chat_ui.request_virtual_keyboard() + return false func _on_emote_selected(emote_id: StringName) -> void: @@ -300,13 +610,556 @@ func _on_emote_selected(emote_id: StringName) -> void: _player.toggle_sitting() -func _toggle_surface_drawing() -> void: +func _on_quick_action_selected(action_id: StringName) -> void: + match action_id: + &"stuff": + _player_menu.open_section(PlayerMenuType.Section.COOLER) + &"logbook": + _player_menu.open_section(PlayerMenuType.Section.LOGBOOK) + &"fishnet": + _player_menu.open_section(PlayerMenuType.Section.NET) + &"mail": + _player_menu.open_section(PlayerMenuType.Section.MAIL) + &"profile": + _player_menu.open_section(PlayerMenuType.Section.PROFILE) + &"online": + _player_menu.open_section(PlayerMenuType.Section.PLAYERS) + &"paint": + _select_art_kit_hotbar_slot() + &"chat": + _chat_ui.open_chat() + &"freecam": + if _player != null: + _player.toggle_free_camera() + &"hud": + set_gameplay_hud_hidden(not _gameplay_hud_hidden) + + +func _handle_virtual_mouse_input(event: InputEvent) -> bool: + if ( + _controller_mapping_manager != null + and _controller_mapping_manager.has_custom_mapping() + ): + return _handle_mapped_virtual_mouse_input(event) + var motion_event: InputEventJoypadMotion = event as InputEventJoypadMotion + if motion_event != null: + if motion_event.axis in [ + VIRTUAL_MOUSE_TRIGGER_AXIS, + VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS, + ]: + if ( + _virtual_mouse_active + and motion_event.device != _virtual_mouse_device_id + ): + return false + _sample_trigger_rest_values(motion_event.device) + _virtual_mouse_trigger_strength = ( + _virtual_mouse_strength_for_device(motion_event.device) + ) + var was_active: bool = _virtual_mouse_active + if ( + _virtual_mouse_trigger_strength + >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD + ): + if not _virtual_mouse_active and _can_start_virtual_mouse(): + _begin_virtual_mouse(motion_event.device) + elif ( + _virtual_mouse_active + and _virtual_mouse_trigger_strength + < VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD + ): + _end_virtual_mouse() + elif _virtual_mouse_active: + _set_virtual_mouse_button( + MOUSE_BUTTON_RIGHT, + _secondary_click_strength_for_device( + motion_event.device + ) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD, + ) + return was_active or _virtual_mouse_active + if ( + _virtual_mouse_active + and motion_event.device == _virtual_mouse_device_id + and motion_event.axis in [ + JOY_AXIS_RIGHT_X, + JOY_AXIS_RIGHT_Y, + ] + ): + if motion_event.axis == JOY_AXIS_RIGHT_X: + _virtual_mouse_stick.x = motion_event.axis_value + else: + _virtual_mouse_stick.y = motion_event.axis_value + return true + var button_event: InputEventJoypadButton = event as InputEventJoypadButton + if ( + button_event != null + and button_event.button_index == JOY_BUTTON_RIGHT_SHOULDER + and ( + _virtual_mouse_active + or _virtual_mouse_trigger_strength + >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD + ) + ): + if not _virtual_mouse_active and _can_start_virtual_mouse(): + _begin_virtual_mouse(button_event.device) + if _virtual_mouse_active: + _set_virtual_mouse_button(MOUSE_BUTTON_LEFT, button_event.pressed) + return true + return false + + +func _handle_mapped_virtual_mouse_input(event: InputEvent) -> bool: + var uses_activation: bool = _controller_mapping_manager.event_uses_role( + event, + ControllerMappingManagerType.ROLE_POINTER_MODIFIER, + ) + if uses_activation: + var was_active: bool = _virtual_mouse_active + _virtual_mouse_trigger_strength = ( + _controller_mapping_manager.get_role_strength( + ControllerMappingManagerType.ROLE_POINTER_MODIFIER + ) + ) + if ( + _virtual_mouse_trigger_strength + >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD + and not _virtual_mouse_active + and _can_start_virtual_mouse() + ): + _begin_virtual_mouse( + _controller_mapping_manager.get_active_device_id() + ) + elif ( + _virtual_mouse_active + and _virtual_mouse_trigger_strength + < VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD + ): + _end_virtual_mouse() + return was_active or _virtual_mouse_active + if not _virtual_mouse_active: + return false + if ( + _controller_mapping_manager.event_uses_role( + event, + ControllerMappingManagerType.ROLE_RIGHT_STICK_X, + ) + or _controller_mapping_manager.event_uses_role( + event, + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y, + ) + ): + _virtual_mouse_stick = _mapped_virtual_mouse_stick() + return true + var button_event := event as InputEventJoypadButton + if button_event != null and _controller_mapping_manager.event_uses_role( + event, + ControllerMappingManagerType.ROLE_RB, + ): + _set_virtual_mouse_button(MOUSE_BUTTON_LEFT, button_event.pressed) + return true + if _controller_mapping_manager.event_uses_role( + event, + ControllerMappingManagerType.ROLE_CAMERA_ZOOM, + ): + _set_virtual_mouse_button( + MOUSE_BUTTON_RIGHT, + _controller_mapping_manager.get_role_strength( + ControllerMappingManagerType.ROLE_CAMERA_ZOOM + ) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD, + ) + return true + return false + + +func _mapped_virtual_mouse_stick() -> Vector2: + return Vector2( + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X + ), + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y + ), + ) + + +func _can_start_virtual_mouse() -> bool: + return ( + _gameplay_ui_enabled + and not _showcase_active + and not _system_menu_open + and not _player_menu_open + and not _shop_open + and not _chat_input_open + and _player != null + and _fishing_spot != null + and _fishing_spot.can_open_system_menu() + and not _emote_radial_menu.is_open() + and not _quick_radial_menu.is_open() + ) + + +func _begin_virtual_mouse(device_id: int) -> void: + if _virtual_mouse_active: + return + _virtual_mouse_active = true + _virtual_mouse_device_id = maxi(device_id, 0) + _virtual_mouse_stick = ( + _mapped_virtual_mouse_stick() + if ( + _controller_mapping_manager != null + and _controller_mapping_manager.has_custom_mapping() + ) + else Vector2( + Input.get_joy_axis(_virtual_mouse_device_id, JOY_AXIS_RIGHT_X), + Input.get_joy_axis(_virtual_mouse_device_id, JOY_AXIS_RIGHT_Y), + ) + ) + _virtual_mouse_prior_camera_input_enabled = ( + _player.is_camera_input_enabled() + ) + _player.set_camera_input_enabled(false) + if _fishing_spot != null: + _fishing_spot.set_local_menu_input_suppressed( + VIRTUAL_MOUSE_INPUT_OWNER, + true, + ) + _virtual_mouse_prior_mouse_mode = Input.mouse_mode + Input.mouse_mode = Input.MOUSE_MODE_HIDDEN + _virtual_mouse_button_mask = 0 + _virtual_mouse_right_pressed = false + _virtual_mouse_window_position = _clamp_virtual_mouse_window_position( + Vector2(get_window().size) * 0.5 + ) + _controller_virtual_cursor.visible = true + _update_virtual_cursor_position(_virtual_mouse_window_position) + _emit_virtual_mouse_motion(Vector2.ZERO) + + +func _end_virtual_mouse() -> void: + if not _virtual_mouse_active: + return + if (_virtual_mouse_button_mask & MOUSE_BUTTON_MASK_LEFT) != 0: + _set_virtual_mouse_button(MOUSE_BUTTON_LEFT, false) + if _virtual_mouse_right_pressed: + _set_virtual_mouse_button(MOUSE_BUTTON_RIGHT, false) + _virtual_mouse_active = false + _virtual_mouse_trigger_strength = 0.0 + _virtual_mouse_stick = Vector2.ZERO + _controller_virtual_cursor.visible = false + if _fishing_spot != null: + _fishing_spot.set_local_menu_input_suppressed( + VIRTUAL_MOUSE_INPUT_OWNER, + false, + ) + if _player != null and is_instance_valid(_player): + _player.set_camera_input_enabled( + _virtual_mouse_prior_camera_input_enabled + ) + Input.mouse_mode = _virtual_mouse_prior_mouse_mode + + +func _update_virtual_mouse(delta: float) -> void: + if not _virtual_mouse_active: + return + if ( + _virtual_mouse_trigger_strength + < VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD + ): + _end_virtual_mouse() + return + var stick: Vector2 = _virtual_mouse_stick + var stick_length: float = stick.length() + if stick_length <= VIRTUAL_MOUSE_STICK_DEADZONE: + return + var adjusted_strength: float = ( + (stick_length - VIRTUAL_MOUSE_STICK_DEADZONE) + / (1.0 - VIRTUAL_MOUSE_STICK_DEADZONE) + ) + var display_scale: float = UIReferencePresentationType.get_scale( + Vector2(get_window().size) + ) + var relative_motion: Vector2 = ( + stick.normalized() + * adjusted_strength + * VIRTUAL_MOUSE_SPEED + * display_scale + * delta + ) + _virtual_mouse_window_position = _clamp_virtual_mouse_window_position( + _virtual_mouse_window_position + relative_motion + ) + _update_virtual_cursor_position(_virtual_mouse_window_position) + _emit_virtual_mouse_motion(relative_motion) + + +func _poll_virtual_mouse_controller_state() -> void: + if ( + _controller_mapping_manager != null + and _controller_mapping_manager.has_custom_mapping() + ): + _poll_mapped_virtual_mouse_controller_state() + return + var device_ids: Array[int] = Input.get_connected_joypads() + # Some Android controller backends deliver device-0 axes without including + # that device in get_connected_joypads(). Player camera input already uses + # this same primary-device fallback. + if device_ids.is_empty(): + device_ids.append(0) + for device_id: int in device_ids: + _sample_trigger_rest_values(device_id) + if _virtual_mouse_active: + if not device_ids.has(_virtual_mouse_device_id): + _end_virtual_mouse() + return + _virtual_mouse_trigger_strength = _virtual_mouse_strength_for_device( + _virtual_mouse_device_id + ) + _set_virtual_mouse_button( + MOUSE_BUTTON_RIGHT, + _secondary_click_strength_for_device( + _virtual_mouse_device_id + ) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD, + ) + _virtual_mouse_stick = Vector2( + Input.get_joy_axis( + _virtual_mouse_device_id, + JOY_AXIS_RIGHT_X, + ), + Input.get_joy_axis( + _virtual_mouse_device_id, + JOY_AXIS_RIGHT_Y, + ), + ) + return + if not _can_start_virtual_mouse(): + return + for device_id: int in device_ids: + var trigger_strength: float = _virtual_mouse_strength_for_device( + device_id + ) + if trigger_strength < VIRTUAL_MOUSE_TRIGGER_THRESHOLD: + continue + _virtual_mouse_trigger_strength = trigger_strength + _begin_virtual_mouse(device_id) + return + + +func _poll_mapped_virtual_mouse_controller_state() -> void: + var trigger_strength: float = _controller_mapping_manager.get_role_strength( + ControllerMappingManagerType.ROLE_POINTER_MODIFIER + ) + if _virtual_mouse_active: + _virtual_mouse_trigger_strength = trigger_strength + if trigger_strength < VIRTUAL_MOUSE_TRIGGER_RELEASE_THRESHOLD: + _end_virtual_mouse() + return + _virtual_mouse_stick = _mapped_virtual_mouse_stick() + _set_virtual_mouse_button( + MOUSE_BUTTON_RIGHT, + _controller_mapping_manager.get_role_strength( + ControllerMappingManagerType.ROLE_CAMERA_ZOOM + ) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD, + ) + return + if ( + trigger_strength >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD + and _can_start_virtual_mouse() + ): + _virtual_mouse_trigger_strength = trigger_strength + _begin_virtual_mouse( + _controller_mapping_manager.get_active_device_id() + ) + + +func _sample_trigger_rest_values(device_id: int) -> void: + if not _virtual_mouse_trigger_rest_by_device.has(device_id): + _virtual_mouse_trigger_rest_by_device[device_id] = Input.get_joy_axis( + device_id, + VIRTUAL_MOUSE_TRIGGER_AXIS, + ) + if not _shared_trigger_rest_by_device.has(device_id): + _shared_trigger_rest_by_device[device_id] = Input.get_joy_axis( + device_id, + VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS, + ) + + +func _virtual_mouse_strength_for_device(device_id: int) -> float: + _sample_trigger_rest_values(device_id) + var dedicated_rest: float = ( + _virtual_mouse_trigger_rest_by_device[device_id] + ) + var shared_rest: float = _shared_trigger_rest_by_device[device_id] + var dedicated_strength: float = normalized_trigger_strength( + Input.get_joy_axis(device_id, VIRTUAL_MOUSE_TRIGGER_AXIS), + dedicated_rest, + ) + var shared_negative_strength: float = directional_trigger_strength( + Input.get_joy_axis(device_id, VIRTUAL_MOUSE_SHARED_TRIGGER_AXIS), + shared_rest, + -1.0, + ) + return maxf(dedicated_strength, shared_negative_strength) + + +func _secondary_click_strength_for_device(device_id: int) -> float: + _sample_trigger_rest_values(device_id) + return directional_trigger_strength( + Input.get_joy_axis(device_id, VIRTUAL_MOUSE_SECONDARY_CLICK_AXIS), + _shared_trigger_rest_by_device[device_id], + 1.0, + ) + + +static func normalized_trigger_strength( + axis_value: float, + resting_value: float, +) -> float: + var negative_travel: float = absf(-1.0 - resting_value) + var positive_travel: float = absf(1.0 - resting_value) + var available_travel: float = maxf(negative_travel, positive_travel) + if available_travel <= 0.001: + return 0.0 + return clampf( + absf(axis_value - resting_value) / available_travel, + 0.0, + 1.0, + ) + + +static func directional_trigger_strength( + axis_value: float, + resting_value: float, + direction: float, +) -> float: + var normalized_direction: float = signf(direction) + if is_zero_approx(normalized_direction): + return 0.0 + var endpoint: float = normalized_direction + var available_travel: float = absf(endpoint - resting_value) + if available_travel <= 0.001: + return 0.0 + var directed_travel: float = ( + (axis_value - resting_value) * normalized_direction + ) + return clampf(directed_travel / available_travel, 0.0, 1.0) + + +func _set_virtual_mouse_button(button: MouseButton, pressed: bool) -> void: + var mask: int = ( + MOUSE_BUTTON_MASK_LEFT + if button == MOUSE_BUTTON_LEFT + else MOUSE_BUTTON_MASK_RIGHT + ) + var already_pressed: bool = bool(_virtual_mouse_button_mask & mask) + if already_pressed == pressed: + return + if pressed: + _virtual_mouse_button_mask |= mask + else: + _virtual_mouse_button_mask &= ~mask + if button == MOUSE_BUTTON_RIGHT: + _virtual_mouse_right_pressed = pressed + var mouse_event := InputEventMouseButton.new() + mouse_event.position = _virtual_mouse_window_position + mouse_event.global_position = _virtual_mouse_window_position + mouse_event.button_index = button + mouse_event.button_mask = _virtual_mouse_button_mask + mouse_event.pressed = pressed + mouse_event.factor = 1.0 + _parse_virtual_mouse_event(mouse_event) + + +func _emit_virtual_mouse_motion(relative_motion: Vector2) -> void: + var motion_event := InputEventMouseMotion.new() + motion_event.position = _virtual_mouse_window_position + motion_event.global_position = _virtual_mouse_window_position + motion_event.relative = relative_motion + motion_event.button_mask = _virtual_mouse_button_mask + _parse_virtual_mouse_event(motion_event) + + +func _parse_virtual_mouse_event(event: InputEventMouse) -> void: + Input.parse_input_event(event) + + +func _update_virtual_cursor_position(viewport_position: Vector2) -> void: + var output_scale: float = UIReferencePresentationType.get_scale( + Vector2(get_window().size) + ) + _controller_virtual_cursor.set_pointer_position( + viewport_position / output_scale + ) + + +func _clamp_virtual_mouse_window_position( + window_position: Vector2, +) -> Vector2: + var bounds: Rect2 = get_virtual_mouse_window_bounds( + Vector2(get_window().size) + ) + return Vector2( + clampf(window_position.x, bounds.position.x, bounds.end.x), + clampf(window_position.y, bounds.position.y, bounds.end.y), + ) + + +static func get_virtual_mouse_window_bounds(window_size: Vector2) -> Rect2: + var output_scale: float = UIReferencePresentationType.get_scale(window_size) + var cursor_margin: Vector2 = ( + ControllerVirtualCursorType.CURSOR_SIZE * output_scale * 0.5 + ) + var bounds_size: Vector2 = Vector2( + maxf(window_size.x - cursor_margin.x * 2.0, 0.0), + maxf(window_size.y - cursor_margin.y * 2.0, 0.0), + ) + return Rect2(cursor_margin, bounds_size) + + +func _select_art_kit_hotbar_slot() -> void: + if _hotbar == null: + return + for slot_index: int in range(PlayerHotbarType.SLOT_COUNT): + if _hotbar.get_item_id(slot_index) == ART_KIT_ITEM_ID: + _hotbar.select_slot(slot_index) + return + + +func set_surface_drawing_hotbar_selected(is_selected: bool) -> void: + _surface_drawing_hotbar_selected = is_selected + _refresh_surface_drawing_activation() + + +func _refresh_surface_drawing_activation() -> void: if _surface_drawing == null: return - if _surface_drawing.is_active(): + var should_be_active: bool = _can_surface_drawing_be_active() + if should_be_active and not _surface_drawing.is_active(): + _surface_drawing.activate(_drawing_pointer_window_position()) + elif not should_be_active and _surface_drawing.is_active(): _surface_drawing.deactivate() - elif _surface_drawing.can_activate(): - _surface_drawing.activate() + + +func _can_surface_drawing_be_active() -> bool: + return ( + _surface_drawing_hotbar_selected + and _gameplay_ui_enabled + and not _system_menu_open + and not _player_menu_open + and not _shop_open + and not _chat_input_open + and not _showcase_active + and _fishing_spot != null + and _fishing_spot.can_use_surface_drawing() + and _surface_drawing != null + and _surface_drawing.can_activate() + ) + + +func _drawing_pointer_window_position() -> Vector2: + if _virtual_mouse_active: + return _virtual_mouse_window_position + return get_window().get_mouse_position() func setup_data_and_identity( @@ -330,7 +1183,31 @@ func setup_data_and_identity( ) -func _process(_delta: float) -> void: +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + _player.set_controller_mapping_manager(_controller_mapping_manager) + _emote_radial_menu.setup_controller_mapping(_controller_mapping_manager) + _quick_radial_menu.setup_controller_mapping(_controller_mapping_manager) + _player_menu.setup_controller_mapping(_controller_mapping_manager) + for panel: SettingsPanelType in [ + _title_settings_panel, _pause_settings_panel + ]: + panel.setup_controller_mapping(_controller_mapping_manager) + + +func is_controller_mapping_capturing() -> bool: + return ( + _title_settings_panel.is_controller_mapping_capturing() + or _pause_settings_panel.is_controller_mapping_capturing() + ) + + +func _process(delta: float) -> void: + _poll_virtual_mouse_controller_state() + _update_virtual_mouse(delta) + _update_controller_menu_scroll(delta) _update_experience_bubble_position() if _item_effects == null or not _gameplay_ui_enabled: _effect_status.hide() @@ -359,6 +1236,95 @@ func _process(_delta: float) -> void: _effect_status.visible = not parts.is_empty() +func _update_controller_menu_scroll(delta: float) -> void: + if ( + _controller_mapping_manager == null + or _virtual_mouse_active + or is_controller_mapping_capturing() + ): + return + var stick_y: float = _controller_menu_scroll_axis() + var magnitude: float = absf(stick_y) + if magnitude <= CONTROLLER_MENU_SCROLL_DEADZONE: + return + var focus_owner: Control = get_viewport().gui_get_focus_owner() + var scroll: ScrollContainer = _focused_scroll_container(focus_owner) + if scroll == null: + return + var adjusted_axis: float = ( + signf(stick_y) + * (magnitude - CONTROLLER_MENU_SCROLL_DEADZONE) + / (1.0 - CONTROLLER_MENU_SCROLL_DEADZONE) + ) + scroll.scroll_vertical += roundi( + adjusted_axis * CONTROLLER_MENU_SCROLL_SPEED * delta + ) + + +func _focused_scroll_container(focus_owner: Control) -> ScrollContainer: + if focus_owner == null or not focus_owner.is_visible_in_tree(): + return null + var current: Node = focus_owner + while current != null: + var scroll := current as ScrollContainer + if scroll != null: + if _controller_scroll_container_is_available(scroll): + return scroll + return null + var candidates: Array[ScrollContainer] = [] + _collect_controller_scroll_containers(current, candidates) + if candidates.size() == 1: + return candidates[0] + if candidates.size() > 1: + return null + current = current.get_parent() + return null + + +func _collect_controller_scroll_containers( + root_node: Node, + result: Array[ScrollContainer], +) -> void: + for child: Node in root_node.get_children(): + var canvas_item := child as CanvasItem + if canvas_item != null and not canvas_item.is_visible_in_tree(): + continue + var scroll := child as ScrollContainer + if scroll != null and _controller_scroll_container_is_available(scroll): + result.append(scroll) + _collect_controller_scroll_containers(child, result) + + +func _controller_scroll_container_is_available( + scroll: ScrollContainer, +) -> bool: + return ( + scroll.is_visible_in_tree() + and scroll.vertical_scroll_mode != ScrollContainer.SCROLL_MODE_DISABLED + ) + + +func _controller_menu_scroll_axis() -> float: + if _controller_mapping_manager != null: + return _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y + ) + return Input.get_joy_axis( + 0, + JOY_AXIS_RIGHT_Y, + ) + + +func _on_controller_connection_changed( + device_id: int, + connected: bool, +) -> void: + _virtual_mouse_trigger_rest_by_device.erase(device_id) + _shared_trigger_rest_by_device.erase(device_id) + if not connected and _virtual_mouse_device_id == device_id: + _end_virtual_mouse() + + func close_player_menu() -> void: _player_menu.close_menu() @@ -383,11 +1349,122 @@ func get_pause_menu() -> PauseMenuType: return _pause_menu +func _apply_active_bait_indicator_style() -> void: + var normal_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_normal_style() + var hover_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_hover_style() + var pressed_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_pressed_style() + var disabled_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_disabled_style() + for style: StyleBoxFlat in [ + normal_style, + hover_style, + pressed_style, + disabled_style, + ]: + style.set_corner_radius_all(36) + style.content_margin_left = 13.5 + style.content_margin_top = 13.5 + style.content_margin_right = 13.5 + style.content_margin_bottom = 13.5 + _active_bait_button.add_theme_stylebox_override("normal", normal_style) + _active_bait_button.add_theme_stylebox_override("hover", hover_style) + _active_bait_button.add_theme_stylebox_override("focus", hover_style) + _active_bait_button.add_theme_stylebox_override("pressed", pressed_style) + _active_bait_button.add_theme_stylebox_override("disabled", disabled_style) + for state: StringName in [ + &"icon_normal_color", + &"icon_hover_color", + &"icon_focus_color", + &"icon_pressed_color", + ]: + _active_bait_button.add_theme_color_override(state, Color.WHITE) + var badge_style := StyleBoxFlat.new() + badge_style.bg_color = Color("0b5558") + badge_style.set_corner_radius_all(12) + badge_style.anti_aliasing = false + _active_bait_quantity_badge.add_theme_stylebox_override( + "panel", + badge_style, + ) + + +func _refresh_active_bait_indicator() -> void: + _refresh_active_bait_indicator_visibility() + if not is_node_ready(): + return + var item: ItemDataType + if ( + _player != null + and _item_catalog != null + and not _player.active_bait_id.is_empty() + ): + item = _item_catalog.get_item_by_id(_player.active_bait_id) + var has_active_bait: bool = item != null and item.is_bait() + _active_bait_button.icon = ( + item.icon + if has_active_bait and item.icon != null + else ACTIVE_BAIT_EMPTY_ICON + ) + _active_bait_quantity_badge.visible = has_active_bait + if not has_active_bait: + _active_bait_button.tooltip_text = "no bait selected" + return + var quantity: int = ( + _bag.get_quantity(item.item_id) + if _bag != null + else 0 + ) + _active_bait_quantity.text = str(quantity) + _active_bait_quantity.add_theme_font_size_override( + "font_size", + 13 if quantity >= 100 else 15, + ) + _active_bait_button.tooltip_text = "%s ×%d" % [ + item.display_name, + quantity, + ] + + +func _on_hud_active_bait_changed(_item_id: StringName) -> void: + _refresh_active_bait_indicator() + + +func _refresh_active_bait_indicator_visibility() -> void: + _active_bait_indicator.visible = ( + _gameplay_ui_enabled + and not _gameplay_hud_hidden + and not _system_menu_open + and not _player_menu_open + and not _shop_open + ) + + +func _on_hud_bait_inventory_changed() -> void: + _refresh_active_bait_indicator() + + func set_gameplay_ui_enabled(enabled: bool) -> void: _gameplay_ui_enabled = enabled - _gameplay_transient_hud.visible = enabled and not _player_menu_open + if not enabled: + _gameplay_hud_hidden = false + _refresh_active_bait_indicator_visibility() + if enabled: + _try_start_shop_npc_speech() + _refresh_gameplay_hud_visibility() _refresh_chat_availability() if not enabled: + _end_virtual_mouse() + if _emote_radial_menu != null and _emote_radial_menu.is_open(): + _emote_radial_menu.close_menu() + if _player != null: + _player.set_camera_input_enabled( + _emote_prior_camera_input_enabled + ) + if _quick_radial_menu != null and _quick_radial_menu.is_open(): + _quick_radial_menu.close_menu() + if _player != null: + _player.set_camera_input_enabled( + _quick_prior_camera_input_enabled + ) if _surface_drawing != null: _surface_drawing.deactivate() close_player_menu_for_session_end() @@ -401,12 +1478,54 @@ func set_gameplay_ui_enabled(enabled: bool) -> void: _hotbar_ui.set_gameplay_input_enabled(true) _refresh_fishing_panel_visibility() call_deferred("_start_next_experience_animation") + _refresh_surface_drawing_activation() + + +func set_gameplay_hud_hidden(hidden: bool) -> void: + _gameplay_hud_hidden = hidden + if _quick_radial_menu != null: + _quick_radial_menu.set_hud_hidden(hidden) + _refresh_gameplay_hud_visibility() + _refresh_active_bait_indicator_visibility() + _refresh_hotbar_visibility() + _refresh_chat_availability() + + +func is_gameplay_hud_hidden() -> bool: + return _gameplay_hud_hidden + + +func _can_toggle_gameplay_hud() -> bool: + return ( + _gameplay_ui_enabled + and not _system_menu_open + and not _player_menu_open + and not _shop_open + and not _chat_input_open + and not _showcase_active + and not _emote_radial_menu.is_open() + and not _quick_radial_menu.is_open() + ) + + +func _refresh_gameplay_hud_visibility() -> void: + var show_world_hud: bool = ( + _gameplay_ui_enabled + and not _gameplay_hud_hidden + and not _system_menu_open + and not _player_menu_open + and not _shop_open + ) + _gameplay_transient_hud.visible = show_world_hud + _experience_presentation.visible = ( + _gameplay_ui_enabled and not _gameplay_hud_hidden + ) func set_system_menu_open(is_open: bool) -> void: _system_menu_open = is_open - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() + _refresh_gameplay_hud_visibility() _refresh_chat_availability() _refresh_hotbar_visibility() _hotbar_ui.set_gameplay_input_enabled( @@ -426,14 +1545,149 @@ func get_fishing_shop() -> FishingShopType: return _fishing_shop -func set_shop_prompt_visible(is_visible: bool) -> void: +func set_shop_prompt_visible( + requested_visible: bool, + world_anchor: Vector3 = Vector3(0.0, INF, 0.0), +) -> void: + _apply_shop_prompt_style() _shop_prompt.visible = ( - is_visible + requested_visible and _gameplay_ui_enabled and not _system_menu_open and not _player_menu_open and not _shop_open ) + if _shop_prompt.visible: + if world_anchor.is_finite(): + _position_shop_prompt(world_anchor) + + +func set_shop_npc_player_in_range(in_range: bool) -> void: + if _shop_npc_player_in_range == in_range: + return + _shop_npc_player_in_range = in_range + if not in_range: + _shop_npc_spoken_for_current_visit = false + return + _try_start_shop_npc_speech() + + +func _try_start_shop_npc_speech() -> void: + if ( + not _shop_npc_player_in_range + or _shop_npc_spoken_for_current_visit + or not _gameplay_ui_enabled + ): + return + _shop_npc_spoken_for_current_visit = true + var now_msec: int = Time.get_ticks_msec() + if now_msec < _shop_npc_next_speech_msec: + return + _shop_npc_next_speech_msec = ( + now_msec + SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS + ) + _ensure_shop_animalese_voice() + TypewriterRevealType.start( + _shop_prompt_message, + SHOP_SPEECH_CHARACTERS_PER_SECOND, + ) + _shop_animalese_voice.speak_text( + _shop_prompt_message, + _shop_prompt_message.text, + "shopkeeper", + SHOP_ANIMALESE_VOICE_ID, + SHOP_SPEECH_CHARACTERS_PER_SECOND, + ) + + +func _ensure_shop_animalese_voice() -> void: + if _shop_animalese_voice != null: + return + _shop_animalese_voice = AnimaleseVoiceType.new() + _shop_animalese_voice.name = "ShopAnimaleseVoice" + _shop_animalese_voice.base_pitch = SHOP_ANIMALESE_BASE_PITCH + add_child(_shop_animalese_voice) + + +func _apply_shop_prompt_style() -> void: + if _shop_prompt.has_meta(&"shop_prompt_styled"): + return + _shop_prompt.set_meta(&"shop_prompt_styled", true) + var bubble_style := StyleBoxFlat.new() + bubble_style.bg_color = Color(UtilityPageStyle.OCEAN_PANEL_MID, 0.96) + bubble_style.set_border_width_all(0) + bubble_style.set_corner_radius_all(12) + bubble_style.anti_aliasing = false + _shop_prompt_bubble.add_theme_stylebox_override("panel", bubble_style) + var badge_style := StyleBoxFlat.new() + badge_style.bg_color = Color("0b5558") + badge_style.set_border_width_all(0) + badge_style.set_corner_radius_all(12) + badge_style.anti_aliasing = false + _shop_prompt_key_badge.add_theme_stylebox_override("panel", badge_style) + for label: Label in [_shop_prompt_message, _shop_prompt_key]: + label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) + label.add_theme_color_override( + "font_color", + UtilityPageStyle.OCEAN_TEXT_PRIMARY, + ) + _shop_prompt_pointer.color = Color( + UtilityPageStyle.OCEAN_PANEL_MID, + 0.96, + ) + + +func _position_shop_prompt(world_anchor: Vector3) -> void: + if _player == null: + _shop_prompt.hide() + return + var camera: Camera3D = _player.get_gameplay_camera() + if camera == null or camera.is_position_behind(world_anchor): + _shop_prompt.hide() + return + var camera_viewport_size := camera.get_viewport().get_visible_rect().size + var ui_viewport_size: Vector2 = _canonical_stage.size + if camera_viewport_size.x <= 0.0 or camera_viewport_size.y <= 0.0: + _shop_prompt.hide() + return + var screen_position := ( + camera.unproject_position(world_anchor) + * ui_viewport_size + / camera_viewport_size + ) + if ( + screen_position.x < -80.0 + or screen_position.x > ui_viewport_size.x + 80.0 + or screen_position.y < -80.0 + or screen_position.y > ui_viewport_size.y + 80.0 + ): + _shop_prompt.hide() + return + var pointer_height := 10.0 + var desired := screen_position - Vector2( + _shop_prompt.size.x * 0.5, + _shop_prompt.size.y + pointer_height, + ) + _shop_prompt.position = Vector2( + clampf( + desired.x, + 8.0, + ui_viewport_size.x - _shop_prompt.size.x - 8.0, + ), + clampf( + desired.y, + 8.0, + ui_viewport_size.y - _shop_prompt.size.y - pointer_height - 8.0, + ), + ) + _shop_prompt_pointer.position = Vector2( + clampf( + screen_position.x - _shop_prompt.position.x, + 20.0, + _shop_prompt.size.x - 20.0, + ), + _shop_prompt.size.y - 1.0, + ) func get_screen_fade() -> ScreenFade: @@ -449,8 +1703,20 @@ func set_effective_ui_pixel_size(pixel_size: int) -> void: _pause_settings_panel.set_effective_ui_pixel_size(pixel_size) -func set_edge_docks(chat_dock_right: bool, paint_dock_right: bool) -> void: +func _on_player_settings_changed(settings: PlayerSettings) -> void: + if settings != null: + _player_menu.set_profile_preview_world_pixel_size( + settings.world_pixel_size + ) + + +func set_edge_docks( + chat_dock_right: bool, + paint_dock_right: bool, + chat_mobile_mode: bool, +) -> void: _chat_ui.set_dock_right(chat_dock_right) + _chat_ui.set_mobile_mode(chat_mobile_mode) _surface_drawing_toolbar.set_dock_right(paint_dock_right) @@ -502,6 +1768,7 @@ func _refresh_fishing_panel_visibility() -> void: var has_content: bool = ( not _status_label.text.strip_edges().is_empty() or not _showcase_details.text.strip_edges().is_empty() + or _bite_prompt_button.visible ) _fishing_panel.visible = ( _gameplay_ui_enabled @@ -509,6 +1776,16 @@ func _refresh_fishing_panel_visibility() -> void: ) +func _on_bite_prompt_changed(prompt_visible: bool) -> void: + _bite_prompt_button.visible = prompt_visible + _refresh_fishing_panel_visibility() + + +func _on_bite_prompt_pressed() -> void: + if _fishing_spot != null: + _fishing_spot.confirm_pending_bite() + + func _on_catch_display_changed( progress: float, chase_progress: float, @@ -516,10 +1793,10 @@ func _on_catch_display_changed( barrier_health: PackedInt32Array, _barrier_max_health: PackedInt32Array, active_barrier_index: int, - visible: bool, + encounter_requested_visible: bool, ) -> void: var encounter_visible: bool = ( - visible + encounter_requested_visible and _fishing_spot != null and _fishing_spot.is_fighting() ) @@ -600,15 +1877,18 @@ func _on_showcase_changed( rarity_name: String, weight_lb: float, quality: int, - visible: bool, + showcase_visible: bool, ) -> void: - _showcase_active = visible - if not visible: + _showcase_active = showcase_visible + _refresh_surface_drawing_activation() + if not showcase_visible: + _set_fishing_panel_showcase_position(false) _showcase_details.text = "" _showcase_details.visible = false _set_fishing_status("") call_deferred("_start_next_experience_animation") return + _set_fishing_panel_showcase_position(true) _catch_track.visible = false _barrier_prompt_panel.visible = false _barrier_prompt.visible = false @@ -626,6 +1906,19 @@ func _on_showcase_changed( _refresh_fishing_panel_visibility() +func _set_fishing_panel_showcase_position(showcase: bool) -> void: + _fishing_panel.offset_top = ( + FISHING_PANEL_SHOWCASE_TOP_OFFSET + if showcase + else FISHING_PANEL_DEFAULT_TOP_OFFSET + ) + _fishing_panel.offset_bottom = ( + FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET + if showcase + else FISHING_PANEL_DEFAULT_BOTTOM_OFFSET + ) + + func _on_experience_awarded( amount: int, previous_total: int, @@ -642,6 +1935,7 @@ func _on_experience_awarded( "previous_level": previous_level, "new_level": new_level, }) + _start_next_experience_animation() func _start_next_experience_animation() -> void: @@ -785,6 +2079,13 @@ func _update_experience_progress(total_experience: int) -> void: func _update_experience_bubble_position() -> void: if not _experience_animation_active or _player == null: return + if _player_menu_open: + _experience_bubble.show() + _experience_bubble.position = Vector2( + (_canonical_stage.size.x - _experience_bubble.size.x) * 0.5, + _experience_panel_rest_y + _experience_panel.size.y + 10.0, + ) + return var camera: Camera3D = _player.get_gameplay_camera() var anchor_position: Vector3 = _player.get_chat_anchor_position() if camera == null or camera.is_position_behind(anchor_position): @@ -819,9 +2120,10 @@ func _update_experience_bubble_position() -> void: func _on_player_menu_visibility_changed(is_open: bool) -> void: _player_menu_open = is_open - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() - _gameplay_transient_hud.visible = _gameplay_ui_enabled and not is_open + if is_open: + _end_virtual_mouse() + _refresh_surface_drawing_activation() + _refresh_gameplay_hud_visibility() if is_open: _hotbar_ui.set_drag_enabled(false) _hotbar_ui.set_presentation_visible(false, false) @@ -864,13 +2166,20 @@ func _on_player_menu_exit_started() -> void: player_menu_backdrop_visibility_changed.emit(false) +func _on_shop_exit_started() -> void: + if not _shop_open: + return + shop_backdrop_visibility_changed.emit(false) + + func _on_shop_visibility_changed(is_open: bool) -> void: _shop_open = is_open - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() - shop_backdrop_visibility_changed.emit(is_open) + _refresh_surface_drawing_activation() + if is_open: + shop_backdrop_visibility_changed.emit(true) if not is_open and _player_menu.is_shop_cooler_mounted(): _player_menu.unmount_shop_cooler() + _refresh_gameplay_hud_visibility() _refresh_chat_availability() _refresh_hotbar_visibility() _hotbar_ui.set_gameplay_input_enabled( @@ -938,11 +2247,53 @@ func _on_inventory_hotbar_context_changed(show_hotbar: bool) -> void: _refresh_hotbar_visibility() +func _on_controller_hotbar_placement_requested( + assignment_kind: PlayerHotbarType.AssignmentKind, + identity: StringName, + initial_slot: int, +) -> void: + _player_menu_hotbar_visible = true + _hotbar_ui.set_player_menu_context(true) + _hotbar_ui.set_drag_enabled(false) + _hotbar_ui.set_presentation_visible(true, false) + _hotbar_ui.begin_controller_placement( + assignment_kind, + identity, + initial_slot, + ) + + +func _on_controller_hotbar_placement_ended() -> void: + _hotbar_ui.end_controller_placement() + _hotbar_ui.set_drag_enabled( + _player_menu_open + and _player_menu_hotbar_visible + and not _system_menu_open + ) + + +func _on_controller_hotbar_management_requested(initial_slot: int) -> void: + _player_menu_hotbar_visible = true + _hotbar_ui.set_player_menu_context(true) + _hotbar_ui.set_drag_enabled(false) + _hotbar_ui.set_presentation_visible(true, false) + _hotbar_ui.begin_controller_management(initial_slot) + + +func _on_controller_hotbar_management_ended() -> void: + _hotbar_ui.end_controller_management() + _hotbar_ui.set_drag_enabled( + _player_menu_open + and _player_menu_hotbar_visible + and not _system_menu_open + ) + + func _on_hotbar_presentation_transition_finished( - is_visible: bool, + presentation_visible: bool, ) -> void: _hotbar_ui.set_drag_enabled( - is_visible + presentation_visible and _player_menu_open and _player_menu_hotbar_visible and not _system_menu_open @@ -952,6 +2303,10 @@ func _on_hotbar_presentation_transition_finished( func _refresh_hotbar_visibility() -> void: _hotbar_ui.set_presentation_visible( _gameplay_ui_enabled + and ( + not _gameplay_hud_hidden + or (_player_menu_open and _player_menu_hotbar_visible) + ) and not _system_menu_open and not _shop_open and ( @@ -963,6 +2318,7 @@ func _refresh_hotbar_visibility() -> void: func _emit_interactive_pointer_ui_changed() -> void: + _refresh_active_bait_indicator_visibility() interactive_pointer_ui_changed.emit( _system_menu_open or _player_menu_open or _shop_open or _chat_input_open ) @@ -970,8 +2326,8 @@ func _emit_interactive_pointer_ui_changed() -> void: func _on_chat_text_entry_ownership_changed(active: bool) -> void: _chat_input_open = active - if active and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() + _refresh_chat_availability() _emit_interactive_pointer_ui_changed() @@ -985,4 +2341,5 @@ func _refresh_chat_availability() -> void: and not _shop_open ) _chat_ui.set_available(chat_available) + _chat_ui.set_hud_hidden(_gameplay_hud_hidden and not _chat_input_open) passive_pointer_ui_changed.emit(chat_available) diff --git a/ui/game_ui.tscn b/ui/game_ui.tscn index 3d59a55..1cbbc4a 100644 --- a/ui/game_ui.tscn +++ b/ui/game_ui.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=3] +[gd_scene load_steps=21 format=3] [ext_resource type="Script" path="res://ui/game_ui.gd" id="1_ui"] [ext_resource type="PackedScene" path="res://ui/player_menu.tscn" id="2_menu"] @@ -11,6 +11,8 @@ [ext_resource type="Script" path="res://ui/chat_ui.gd" id="9_chat"] [ext_resource type="PackedScene" path="res://ui/emote_radial_menu.tscn" id="10_emote"] [ext_resource type="PackedScene" path="res://ui/surface_drawing_toolbar.tscn" id="11_drawing_toolbar"] +[ext_resource type="PackedScene" path="res://ui/quick_radial_menu.tscn" id="12_quick"] +[ext_resource type="Script" path="res://ui/controller_virtual_cursor.gd" id="13_cursor"] [sub_resource type="StyleBoxFlat" id="StyleBox_chase_background"] bg_color = Color(0.032, 0.118, 0.15, 1) @@ -80,7 +82,83 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -[node name="ExperienceProgressPanel" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"] +[node name="ActiveBaitIndicator" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"] +unique_name_in_owner = true +z_index = 54 +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -88.0 +offset_top = -88.0 +offset_right = -16.0 +offset_bottom = -16.0 +grow_horizontal = 0 +grow_vertical = 0 +mouse_filter = 2 +theme = ExtResource("3_theme") + +[node name="ActiveBaitButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +focus_mode = 0 +texture_filter = 1 +expand_icon = true +alignment = 1 +icon_alignment = 1 +vertical_icon_alignment = 1 + +[node name="ActiveBaitQuantityBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"] +unique_name_in_owner = true +z_index = 2 +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -27.0 +offset_top = 3.0 +offset_right = -3.0 +offset_bottom = 27.0 +grow_horizontal = 0 +mouse_filter = 2 + +[node name="ActiveBaitQuantity" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator/ActiveBaitQuantityBadge"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 1.0 +offset_right = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_colors/font_color = Color(0.905882, 0.960784, 0.956863, 1) +theme_override_font_sizes/font_size = 15 +text = "0" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ExperiencePresentation" type="Control" parent="UIRoot/CanonicalStage"] +unique_name_in_owner = true +z_index = 200 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="ExperienceProgressPanel" type="PanelContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation"] unique_name_in_owner = true visible = false z_index = 63 @@ -96,40 +174,40 @@ mouse_filter = 2 theme = ExtResource("3_theme") theme_override_styles/panel = SubResource("StyleBox_experience_panel") -[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel"] +[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel"] layout_mode = 2 theme_override_constants/margin_left = 14 theme_override_constants/margin_top = 7 theme_override_constants/margin_right = 14 theme_override_constants/margin_bottom = 9 -[node name="Layout" type="VBoxContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin"] +[node name="Layout" type="VBoxContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin"] layout_mode = 2 theme_override_constants/separation = 4 -[node name="Header" type="HBoxContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin/Layout"] +[node name="Header" type="HBoxContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin/Layout"] layout_mode = 2 -[node name="ExperienceLevelLabel" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin/Layout/Header"] +[node name="ExperienceLevelLabel" type="Label" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin/Layout/Header"] unique_name_in_owner = true layout_mode = 2 text = "level 1" theme_override_colors/font_color = Color(0.95, 0.98, 1, 1) theme_override_font_sizes/font_size = 16 -[node name="Spacer" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin/Layout/Header"] +[node name="Spacer" type="Control" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin/Layout/Header"] layout_mode = 2 size_flags_horizontal = 3 mouse_filter = 2 -[node name="ExperienceAwardLabel" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin/Layout/Header"] +[node name="ExperienceAwardLabel" type="Label" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin/Layout/Header"] unique_name_in_owner = true layout_mode = 2 text = "+0 xp" theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) theme_override_font_sizes/font_size = 16 -[node name="ExperienceProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceProgressPanel/Margin/Layout"] +[node name="ExperienceProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceProgressPanel/Margin/Layout"] unique_name_in_owner = true custom_minimum_size = Vector2(0, 20) layout_mode = 2 @@ -139,7 +217,7 @@ theme_override_styles/fill = SubResource("StyleBox_experience_fill") value = 0.0 show_percentage = false -[node name="ExperienceBubble" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"] +[node name="ExperienceBubble" type="PanelContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation"] unique_name_in_owner = true visible = false z_index = 63 @@ -149,14 +227,14 @@ mouse_filter = 2 theme = ExtResource("3_theme") theme_override_styles/panel = SubResource("StyleBox_experience_panel") -[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceBubble"] +[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceBubble"] layout_mode = 2 theme_override_constants/margin_left = 10 theme_override_constants/margin_top = 5 theme_override_constants/margin_right = 10 theme_override_constants/margin_bottom = 5 -[node name="ExperienceBubbleLabel" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ExperienceBubble/Margin"] +[node name="ExperienceBubbleLabel" type="Label" parent="UIRoot/CanonicalStage/ExperiencePresentation/ExperienceBubble/Margin"] unique_name_in_owner = true layout_mode = 2 text = "+0 xp!" @@ -201,6 +279,16 @@ text = "" horizontal_alignment = 1 theme_override_font_sizes/font_size = 16 +[node name="BitePromptButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(0, 42) +layout_mode = 2 +focus_mode = 0 +mouse_default_cursor_shape = 2 +text = "you got a bite!" +theme_override_font_sizes/font_size = 22 + [node name="ShowcaseDetails" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"] unique_name_in_owner = true visible = false @@ -317,35 +405,70 @@ unique_name_in_owner = true [node name="EmoteRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("10_emote")] unique_name_in_owner = true -[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"] +[node name="QuickRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("12_quick")] +unique_name_in_owner = true + +[node name="ShopPrompt" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"] unique_name_in_owner = true visible = false z_index = 55 -anchors_preset = 7 -anchor_left = 0.5 -anchor_top = 1.0 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -150.0 -offset_top = -284.0 -offset_right = 150.0 -offset_bottom = -246.0 -grow_horizontal = 2 -grow_vertical = 0 +offset_right = 300.0 +offset_bottom = 72.0 mouse_filter = 2 theme = ExtResource("3_theme") -[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"] +[node name="ShopPromptBubble" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble"] layout_mode = 2 theme_override_constants/margin_left = 12 -theme_override_constants/margin_top = 7 +theme_override_constants/margin_top = 8 theme_override_constants/margin_right = 12 -theme_override_constants/margin_bottom = 7 +theme_override_constants/margin_bottom = 8 -[node name="Label" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/Margin"] +[node name="ShopPromptMessage" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble/Margin"] +unique_name_in_owner = true layout_mode = 2 -text = "press e to open fishing shop" +theme_override_font_sizes/font_size = 18 +text = "Hey, if you've got any fish I've got cash! OwO" horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 2 + +[node name="ShopPromptKeyBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 282.0 +offset_top = -8.0 +offset_right = 306.0 +offset_bottom = 16.0 +mouse_filter = 2 + +[node name="ShopPromptKey" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptKeyBadge"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_font_sizes/font_size = 15 +text = "E" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ShopPromptPointer" type="Polygon2D" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"] +unique_name_in_owner = true +polygon = PackedVector2Array(-8, 0, 8, 0, 0, 10) [node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")] unique_name_in_owner = true @@ -375,6 +498,12 @@ grow_vertical = 2 mouse_filter = 2 script = ExtResource("9_chat") +[node name="ControllerVirtualCursor" type="Control" parent="UIRoot"] +unique_name_in_owner = true +z_index = 500 +mouse_filter = 2 +script = ExtResource("13_cursor") + [node name="ScreenFade" type="ColorRect" parent="UIRoot"] unique_name_in_owner = true visible = false diff --git a/ui/hotbar.gd b/ui/hotbar.gd index 6466c34..fe26cc6 100644 --- a/ui/hotbar.gd +++ b/ui/hotbar.gd @@ -4,6 +4,7 @@ extends Control signal presentation_transition_finished(is_visible: bool) const ItemCatalogType = preload("res://items/item_catalog.gd") +const ItemDataType = preload("res://items/item_data.gd") const PlayerBagType = preload("res://inventory/player_bag.gd") const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") const FishInventoryType = preload("res://inventory/fish_inventory.gd") @@ -16,9 +17,9 @@ const FishingSpotType = preload("res://fishing/fishing_spot.gd") const DESKTOP_REFERENCE_SIZE := Vector2(1280.0, 720.0) const COMPACT_REFERENCE_SIZE := Vector2(640.0, 480.0) -const HOTBAR_PRESENTATION_SCALE: float = 0.60 -const HOTBAR_CANONICAL_POSITION := Vector2(256.0, 288.0) -const HOTBAR_MENU_POSITION := Vector2(80.0, 198.0) +const HOTBAR_PRESENTATION_SCALE: float = 0.80 +const HOTBAR_CANONICAL_POSITION := Vector2.ZERO +const HOTBAR_MENU_POSITION := Vector2(-136.0, -90.0) const HOTBAR_GAMEPLAY_Z_INDEX: int = 35 # PlayerMenu is z=30 and its authored inventory panels are relative z=50. const HOTBAR_MENU_Z_INDEX: int = 90 @@ -41,6 +42,13 @@ var _item_name_suppressed: bool = false var _motion_elapsed: float = 0.0 var _compact_layout: bool = false var _player_menu_context: bool = false +var _controller_placement_active: bool = false +var _controller_management_active: bool = false +var _controller_placement_kind: PlayerHotbarType.AssignmentKind = ( + PlayerHotbarType.AssignmentKind.EMPTY +) +var _controller_placement_identity: StringName +var _controller_placement_texture: Texture2D var _visibility_tween: Tween var _visibility_generation: int = 0 @@ -99,14 +107,115 @@ func set_drag_enabled(enabled: bool) -> void: _hide_item_name() +func begin_controller_placement( + assignment_kind: PlayerHotbarType.AssignmentKind, + identity: StringName, + initial_slot: int, +) -> void: + if _hotbar == null or identity.is_empty(): + return + _controller_placement_active = true + _controller_placement_kind = assignment_kind + _controller_placement_identity = identity + _controller_placement_texture = _resolve_controller_placement_texture() + var slot_count: int = _slots.size() + for index: int in slot_count: + var slot: BubbleHotbarSlotType = _slots[index] + slot.focus_mode = Control.FOCUS_ALL + slot.focus_neighbor_left = slot.get_path_to( + _slots[wrapi(index - 1, 0, slot_count)] + ) + slot.focus_neighbor_right = slot.get_path_to( + _slots[wrapi(index + 1, 0, slot_count)] + ) + slot.focus_neighbor_top = slot.get_path_to(slot) + slot.focus_neighbor_bottom = slot.get_path_to(slot) + var target_index: int = clampi(initial_slot, 0, slot_count - 1) + _hotbar.select_slot(target_index) + _refresh_controller_placement_preview() + _slots[target_index].call_deferred("grab_focus") + + +func end_controller_placement() -> void: + if not _controller_placement_active: + return + _controller_placement_active = false + _controller_placement_kind = PlayerHotbarType.AssignmentKind.EMPTY + _controller_placement_identity = StringName() + _controller_placement_texture = null + for slot: BubbleHotbarSlotType in _slots: + slot.focus_mode = Control.FOCUS_NONE + slot.set_controller_placement_preview(false, null) + _show_selected_item_briefly() + + +func begin_controller_management(initial_slot: int) -> void: + if _hotbar == null or _slots.is_empty(): + return + _controller_management_active = true + var slot_count: int = _slots.size() + for index: int in slot_count: + var slot: BubbleHotbarSlotType = _slots[index] + slot.focus_mode = Control.FOCUS_ALL + slot.focus_neighbor_left = slot.get_path_to( + _slots[wrapi(index - 1, 0, slot_count)] + ) + slot.focus_neighbor_right = slot.get_path_to( + _slots[wrapi(index + 1, 0, slot_count)] + ) + slot.focus_neighbor_top = slot.get_path_to(slot) + slot.focus_neighbor_bottom = slot.get_path_to(slot) + var target_index: int = clampi(initial_slot, 0, slot_count - 1) + _hotbar.select_slot(target_index) + _slots[target_index].call_deferred("grab_focus") + + +func end_controller_management() -> void: + if not _controller_management_active: + return + _controller_management_active = false + for slot: BubbleHotbarSlotType in _slots: + slot.focus_mode = Control.FOCUS_NONE + _show_selected_item_briefly() + + +func _resolve_controller_placement_texture() -> Texture2D: + if ( + _controller_placement_kind == PlayerHotbarType.AssignmentKind.FISH + and _fish_inventory != null + ): + var fish_catch: FishCatchType = _fish_inventory.get_catch_by_id( + _controller_placement_identity + ) + return fish_catch.fish.display_texture if fish_catch != null else null + if ( + _controller_placement_kind == PlayerHotbarType.AssignmentKind.ITEM + and _catalog != null + ): + var item: ItemDataType = _catalog.get_item_by_id( + _controller_placement_identity + ) + return item.icon if item != null else null + return null + + +func _refresh_controller_placement_preview() -> void: + if not _controller_placement_active or _hotbar == null: + return + var target_index: int = _hotbar.get_selected_slot() + for slot: BubbleHotbarSlotType in _slots: + slot.set_controller_placement_preview( + slot.slot_index == target_index, + _controller_placement_texture, + ) + + func set_player_menu_context(enabled: bool) -> void: if _player_menu_context == enabled: return _player_menu_context = enabled z_index = HOTBAR_MENU_Z_INDEX if enabled else HOTBAR_GAMEPLAY_Z_INDEX - _presentation_scale_root.position = ( - HOTBAR_MENU_POSITION if enabled else HOTBAR_CANONICAL_POSITION - ) + _presentation_scale_root.position = _presentation_position() func set_presentation_visible( @@ -193,6 +302,14 @@ func _unhandled_input(event: InputEvent) -> void: or not _fishing_spot.can_change_hotbar_selection() ): return + if event.is_action_pressed("hotbar_previous"): + _hotbar.cycle_selection(-1) + get_viewport().set_input_as_handled() + return + if event.is_action_pressed("hotbar_next"): + _hotbar.cycle_selection(1) + get_viewport().set_input_as_handled() + return if event is InputEventKey and event.pressed and not event.echo: for index: int in range(PlayerHotbarType.SLOT_COUNT): if event.is_action_pressed("hotbar_%d" % (index + 1)): @@ -222,6 +339,9 @@ func _collect_slots() -> void: slot.item_hover_ended.connect(_on_slot_item_hover_ended) slot.item_drag_started.connect(_on_slot_drag_started) slot.item_drag_finished.connect(_on_slot_drag_finished) + slot.focus_entered.connect( + _on_controller_slot_focused.bind(slot.slot_index) + ) _slots.append(slot) _slots.sort_custom( func( @@ -241,11 +361,7 @@ func _apply_layout() -> void: _presentation_scale_root.scale = ( Vector2.ONE * HOTBAR_PRESENTATION_SCALE ) - _presentation_scale_root.position = ( - HOTBAR_MENU_POSITION - if _player_menu_context - else HOTBAR_CANONICAL_POSITION - ) + _presentation_scale_root.position = _presentation_position() var field_size := ( Vector2(580.0, 82.0) if _compact_layout @@ -260,6 +376,21 @@ func _apply_layout() -> void: slot.apply_layout(_compact_layout) +func _presentation_position() -> Vector2: + var scaled_size: Vector2 = ( + DESKTOP_REFERENCE_SIZE * HOTBAR_PRESENTATION_SCALE + ) + var bottom_centered_offset := Vector2( + (DESKTOP_REFERENCE_SIZE.x - scaled_size.x) * 0.5, + DESKTOP_REFERENCE_SIZE.y - scaled_size.y, + ) + return bottom_centered_offset + ( + HOTBAR_MENU_POSITION + if _player_menu_context + else HOTBAR_CANONICAL_POSITION + ) + + func _refresh() -> void: for slot: BubbleHotbarSlotType in _slots: slot.refresh() @@ -270,10 +401,24 @@ func _on_selected_slot_changed( _item_id: StringName, ) -> void: _refresh() + if _controller_placement_active: + _refresh_controller_placement_preview() + return if _hovered_slot_index < 0: _show_selected_item_briefly() +func _on_controller_slot_focused(slot_index: int) -> void: + if ( + not (_controller_placement_active or _controller_management_active) + or _hotbar == null + ): + return + _hotbar.select_slot(slot_index) + if _controller_placement_active: + _refresh_controller_placement_preview() + + func _on_slot_item_hovered( slot_index: int, item_id: StringName, diff --git a/ui/icons/pictograms/arrow_dark_down_full.png b/ui/icons/pictograms/arrow_dark_down_full.png new file mode 100644 index 0000000..e2bdb9e Binary files /dev/null and b/ui/icons/pictograms/arrow_dark_down_full.png differ diff --git a/ui/icons/pictograms/arrow_dark_down_full.png.import b/ui/icons/pictograms/arrow_dark_down_full.png.import new file mode 100644 index 0000000..2e7726a --- /dev/null +++ b/ui/icons/pictograms/arrow_dark_down_full.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d4aah3j5brkij" +path="res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_dark_down_full.png" +dest_files=["res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/arrow_dark_up_full.png b/ui/icons/pictograms/arrow_dark_up_full.png new file mode 100644 index 0000000..a9d2f3d Binary files /dev/null and b/ui/icons/pictograms/arrow_dark_up_full.png differ diff --git a/ui/icons/pictograms/arrow_dark_up_full.png.import b/ui/icons/pictograms/arrow_dark_up_full.png.import new file mode 100644 index 0000000..1cbc9ac --- /dev/null +++ b/ui/icons/pictograms/arrow_dark_up_full.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d34x31jx5p5ll" +path="res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_dark_up_full.png" +dest_files=["res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/arrow_light_down_more.png b/ui/icons/pictograms/arrow_light_down_more.png new file mode 100644 index 0000000..3c7683d Binary files /dev/null and b/ui/icons/pictograms/arrow_light_down_more.png differ diff --git a/ui/icons/pictograms/arrow_light_down_more.png.import b/ui/icons/pictograms/arrow_light_down_more.png.import new file mode 100644 index 0000000..47a516b --- /dev/null +++ b/ui/icons/pictograms/arrow_light_down_more.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cmc7sguttrl2o" +path="res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_light_down_more.png" +dest_files=["res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/arrow_light_left_more.png b/ui/icons/pictograms/arrow_light_left_more.png new file mode 100644 index 0000000..361adab Binary files /dev/null and b/ui/icons/pictograms/arrow_light_left_more.png differ diff --git a/ui/icons/pictograms/arrow_light_left_more.png.import b/ui/icons/pictograms/arrow_light_left_more.png.import new file mode 100644 index 0000000..07d5a52 --- /dev/null +++ b/ui/icons/pictograms/arrow_light_left_more.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3lx64prsccyk" +path="res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_light_left_more.png" +dest_files=["res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/arrow_light_right_more.png b/ui/icons/pictograms/arrow_light_right_more.png new file mode 100644 index 0000000..39d6148 Binary files /dev/null and b/ui/icons/pictograms/arrow_light_right_more.png differ diff --git a/ui/icons/pictograms/arrow_light_right_more.png.import b/ui/icons/pictograms/arrow_light_right_more.png.import new file mode 100644 index 0000000..e0a72ff --- /dev/null +++ b/ui/icons/pictograms/arrow_light_right_more.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cedstrt57et53" +path="res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_light_right_more.png" +dest_files=["res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/arrow_light_up_more.png b/ui/icons/pictograms/arrow_light_up_more.png new file mode 100644 index 0000000..03e3817 Binary files /dev/null and b/ui/icons/pictograms/arrow_light_up_more.png differ diff --git a/ui/icons/pictograms/arrow_light_up_more.png.import b/ui/icons/pictograms/arrow_light_up_more.png.import new file mode 100644 index 0000000..60f266f --- /dev/null +++ b/ui/icons/pictograms/arrow_light_up_more.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqu71oklbvofk" +path="res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/arrow_light_up_more.png" +dest_files=["res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/check_mark_dark.png b/ui/icons/pictograms/check_mark_dark.png new file mode 100644 index 0000000..f1c398b Binary files /dev/null and b/ui/icons/pictograms/check_mark_dark.png differ diff --git a/ui/icons/pictograms/check_mark_dark.png.import b/ui/icons/pictograms/check_mark_dark.png.import new file mode 100644 index 0000000..d2fa9e9 --- /dev/null +++ b/ui/icons/pictograms/check_mark_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3x5tryb721th" +path="res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/check_mark_dark.png" +dest_files=["res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/check_mark_light.png b/ui/icons/pictograms/check_mark_light.png new file mode 100644 index 0000000..da0af7c Binary files /dev/null and b/ui/icons/pictograms/check_mark_light.png differ diff --git a/ui/icons/pictograms/check_mark_light.png.import b/ui/icons/pictograms/check_mark_light.png.import new file mode 100644 index 0000000..c3af542 --- /dev/null +++ b/ui/icons/pictograms/check_mark_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dtibvrik7ug0j" +path="res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/check_mark_light.png" +dest_files=["res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/online_dark.png b/ui/icons/pictograms/online_dark.png new file mode 100644 index 0000000..1c643ca Binary files /dev/null and b/ui/icons/pictograms/online_dark.png differ diff --git a/ui/icons/pictograms/online_dark.png.import b/ui/icons/pictograms/online_dark.png.import new file mode 100644 index 0000000..f917f4c --- /dev/null +++ b/ui/icons/pictograms/online_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://uxkgiwd7py03" +path="res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/online_dark.png" +dest_files=["res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/save_dark.png b/ui/icons/pictograms/save_dark.png new file mode 100644 index 0000000..382648b Binary files /dev/null and b/ui/icons/pictograms/save_dark.png differ diff --git a/ui/icons/pictograms/save_dark.png.import b/ui/icons/pictograms/save_dark.png.import new file mode 100644 index 0000000..33bbad8 --- /dev/null +++ b/ui/icons/pictograms/save_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b71gds1q8ny61" +path="res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/save_dark.png" +dest_files=["res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/settings_dark.png b/ui/icons/pictograms/settings_dark.png new file mode 100644 index 0000000..3777b39 Binary files /dev/null and b/ui/icons/pictograms/settings_dark.png differ diff --git a/ui/icons/pictograms/settings_dark.png.import b/ui/icons/pictograms/settings_dark.png.import new file mode 100644 index 0000000..fabd6c0 --- /dev/null +++ b/ui/icons/pictograms/settings_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ul28xl5qed55" +path="res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/settings_dark.png" +dest_files=["res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/undo_dark.png b/ui/icons/pictograms/undo_dark.png new file mode 100644 index 0000000..e26b579 Binary files /dev/null and b/ui/icons/pictograms/undo_dark.png differ diff --git a/ui/icons/pictograms/undo_dark.png.import b/ui/icons/pictograms/undo_dark.png.import new file mode 100644 index 0000000..e1cce7e --- /dev/null +++ b/ui/icons/pictograms/undo_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://deux1h722j33u" +path="res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/undo_dark.png" +dest_files=["res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/undo_light.png b/ui/icons/pictograms/undo_light.png new file mode 100644 index 0000000..aa04205 Binary files /dev/null and b/ui/icons/pictograms/undo_light.png differ diff --git a/ui/icons/pictograms/undo_light.png.import b/ui/icons/pictograms/undo_light.png.import new file mode 100644 index 0000000..161faa8 --- /dev/null +++ b/ui/icons/pictograms/undo_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1qr5t7gx1eag" +path="res://.godot/imported/undo_light.png-83dd161fb13faa93326dee6cb01e205c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/undo_light.png" +dest_files=["res://.godot/imported/undo_light.png-83dd161fb13faa93326dee6cb01e205c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/x_dark.png b/ui/icons/pictograms/x_dark.png new file mode 100644 index 0000000..d4d183d Binary files /dev/null and b/ui/icons/pictograms/x_dark.png differ diff --git a/ui/icons/pictograms/x_dark.png.import b/ui/icons/pictograms/x_dark.png.import new file mode 100644 index 0000000..36797cb --- /dev/null +++ b/ui/icons/pictograms/x_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cg8vi6sxnpl2t" +path="res://.godot/imported/x_dark.png-5430cf57b7772f059ee1fac9842c6898.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/x_dark.png" +dest_files=["res://.godot/imported/x_dark.png-5430cf57b7772f059ee1fac9842c6898.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/pictograms/x_light.png b/ui/icons/pictograms/x_light.png new file mode 100644 index 0000000..68b3180 Binary files /dev/null and b/ui/icons/pictograms/x_light.png differ diff --git a/ui/icons/pictograms/x_light.png.import b/ui/icons/pictograms/x_light.png.import new file mode 100644 index 0000000..36b30e7 --- /dev/null +++ b/ui/icons/pictograms/x_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvb228ypykqaq" +path="res://.godot/imported/x_light.png-b099203c17614ca6f36b269beabfcbab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/pictograms/x_light.png" +dest_files=["res://.godot/imported/x_light.png-b099203c17614ca6f36b269beabfcbab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/close.png b/ui/icons/tab_menu/close.png new file mode 100644 index 0000000..010564e Binary files /dev/null and b/ui/icons/tab_menu/close.png differ diff --git a/ui/icons/tab_menu/close.png.import b/ui/icons/tab_menu/close.png.import new file mode 100644 index 0000000..97421f4 --- /dev/null +++ b/ui/icons/tab_menu/close.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://i0di6nvyfgwq" +path="res://.godot/imported/close.png-f592282e3f82852ae6231bd14b5fd099.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/close.png" +dest_files=["res://.godot/imported/close.png-f592282e3f82852ae6231bd14b5fd099.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/fishnet_simple.png b/ui/icons/tab_menu/fishnet_simple.png new file mode 100644 index 0000000..00f9a75 Binary files /dev/null and b/ui/icons/tab_menu/fishnet_simple.png differ diff --git a/ui/icons/tab_menu/fishnet_simple.png.import b/ui/icons/tab_menu/fishnet_simple.png.import new file mode 100644 index 0000000..3264172 --- /dev/null +++ b/ui/icons/tab_menu/fishnet_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cjbdqfnyvsnm4" +path="res://.godot/imported/fishnet_simple.png-4b51afc915ee521b23b2244427b7733d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/fishnet_simple.png" +dest_files=["res://.godot/imported/fishnet_simple.png-4b51afc915ee521b23b2244427b7733d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/inventory_simple.png b/ui/icons/tab_menu/inventory_simple.png new file mode 100644 index 0000000..94a41ef Binary files /dev/null and b/ui/icons/tab_menu/inventory_simple.png differ diff --git a/ui/icons/tab_menu/inventory_simple.png.import b/ui/icons/tab_menu/inventory_simple.png.import new file mode 100644 index 0000000..c570770 --- /dev/null +++ b/ui/icons/tab_menu/inventory_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cjcbjd5edx5x4" +path="res://.godot/imported/inventory_simple.png-f67cb963aaeb25aa9f4d1cdb8c686d15.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/inventory_simple.png" +dest_files=["res://.godot/imported/inventory_simple.png-f67cb963aaeb25aa9f4d1cdb8c686d15.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/logbook_icon_simple.png b/ui/icons/tab_menu/logbook_icon_simple.png new file mode 100644 index 0000000..0455fdd Binary files /dev/null and b/ui/icons/tab_menu/logbook_icon_simple.png differ diff --git a/ui/icons/tab_menu/logbook_icon_simple.png.import b/ui/icons/tab_menu/logbook_icon_simple.png.import new file mode 100644 index 0000000..3b6c86a --- /dev/null +++ b/ui/icons/tab_menu/logbook_icon_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbrhsth08pra" +path="res://.godot/imported/logbook_icon_simple.png-19496dc73a295b5d47ceff1151998546.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/logbook_icon_simple.png" +dest_files=["res://.godot/imported/logbook_icon_simple.png-19496dc73a295b5d47ceff1151998546.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/mail_simple.png b/ui/icons/tab_menu/mail_simple.png new file mode 100644 index 0000000..0c5569c Binary files /dev/null and b/ui/icons/tab_menu/mail_simple.png differ diff --git a/ui/icons/tab_menu/mail_simple.png.import b/ui/icons/tab_menu/mail_simple.png.import new file mode 100644 index 0000000..6ed128e --- /dev/null +++ b/ui/icons/tab_menu/mail_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://jlg77ov0slgm" +path="res://.godot/imported/mail_simple.png-95f4049e43a5076c9411faf284044682.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/mail_simple.png" +dest_files=["res://.godot/imported/mail_simple.png-95f4049e43a5076c9411faf284044682.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/online_simple.png b/ui/icons/tab_menu/online_simple.png new file mode 100644 index 0000000..e332392 Binary files /dev/null and b/ui/icons/tab_menu/online_simple.png differ diff --git a/ui/icons/tab_menu/online_simple.png.import b/ui/icons/tab_menu/online_simple.png.import new file mode 100644 index 0000000..51ea7ab --- /dev/null +++ b/ui/icons/tab_menu/online_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://br2rfqaxc0sjl" +path="res://.godot/imported/online_simple.png-8eb00fe8c066e2f85263d1fb847d94d6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/online_simple.png" +dest_files=["res://.godot/imported/online_simple.png-8eb00fe8c066e2f85263d1fb847d94d6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/tab_menu/profile_simple.png b/ui/icons/tab_menu/profile_simple.png new file mode 100644 index 0000000..de13f5d Binary files /dev/null and b/ui/icons/tab_menu/profile_simple.png differ diff --git a/ui/icons/tab_menu/profile_simple.png.import b/ui/icons/tab_menu/profile_simple.png.import new file mode 100644 index 0000000..1a7ab07 --- /dev/null +++ b/ui/icons/tab_menu/profile_simple.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cd08b16j75wep" +path="res://.godot/imported/profile_simple.png-4d3096990c6fb1ca6619433b80d957a2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/tab_menu/profile_simple.png" +dest_files=["res://.godot/imported/profile_simple.png-4d3096990c6fb1ca6619433b80d957a2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/weather/weather_clear_day.png b/ui/icons/weather/weather_clear_day.png new file mode 100644 index 0000000..266887d Binary files /dev/null and b/ui/icons/weather/weather_clear_day.png differ diff --git a/ui/icons/weather/weather_clear_day.png.import b/ui/icons/weather/weather_clear_day.png.import new file mode 100644 index 0000000..175f21f --- /dev/null +++ b/ui/icons/weather/weather_clear_day.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ce6ge5yypnnpf" +path="res://.godot/imported/weather_clear_day.png-1cd1140830664281637d5589a748ef58.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/weather/weather_clear_day.png" +dest_files=["res://.godot/imported/weather_clear_day.png-1cd1140830664281637d5589a748ef58.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/weather/weather_clear_night_full.png b/ui/icons/weather/weather_clear_night_full.png new file mode 100644 index 0000000..6263b34 Binary files /dev/null and b/ui/icons/weather/weather_clear_night_full.png differ diff --git a/ui/icons/weather/weather_clear_night_full.png.import b/ui/icons/weather/weather_clear_night_full.png.import new file mode 100644 index 0000000..b9cffaf --- /dev/null +++ b/ui/icons/weather/weather_clear_night_full.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://78l0keyrvuiw" +path="res://.godot/imported/weather_clear_night_full.png-3640fcf886e9bd01942e21cc0212bc45.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/weather/weather_clear_night_full.png" +dest_files=["res://.godot/imported/weather_clear_night_full.png-3640fcf886e9bd01942e21cc0212bc45.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/weather/weather_cloudy.png b/ui/icons/weather/weather_cloudy.png new file mode 100644 index 0000000..0e96583 Binary files /dev/null and b/ui/icons/weather/weather_cloudy.png differ diff --git a/ui/icons/weather/weather_cloudy.png.import b/ui/icons/weather/weather_cloudy.png.import new file mode 100644 index 0000000..c74657d --- /dev/null +++ b/ui/icons/weather/weather_cloudy.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b17frlbta1p7f" +path="res://.godot/imported/weather_cloudy.png-c41e4d98007294310395f752b39ddaa6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/weather/weather_cloudy.png" +dest_files=["res://.godot/imported/weather_cloudy.png-c41e4d98007294310395f752b39ddaa6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/weather/weather_fog.png b/ui/icons/weather/weather_fog.png new file mode 100644 index 0000000..00e93b6 Binary files /dev/null and b/ui/icons/weather/weather_fog.png differ diff --git a/ui/icons/weather/weather_fog.png.import b/ui/icons/weather/weather_fog.png.import new file mode 100644 index 0000000..8887cfb --- /dev/null +++ b/ui/icons/weather/weather_fog.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wpaj2l01upt6" +path="res://.godot/imported/weather_fog.png-0fe4352261121bbcc8da10ebc1995663.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/weather/weather_fog.png" +dest_files=["res://.godot/imported/weather_fog.png-0fe4352261121bbcc8da10ebc1995663.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/icons/weather/weather_rain.png b/ui/icons/weather/weather_rain.png new file mode 100644 index 0000000..b3de517 Binary files /dev/null and b/ui/icons/weather/weather_rain.png differ diff --git a/ui/icons/weather/weather_rain.png.import b/ui/icons/weather/weather_rain.png.import new file mode 100644 index 0000000..af5d773 --- /dev/null +++ b/ui/icons/weather/weather_rain.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfqvlqp3nrfok" +path="res://.godot/imported/weather_rain.png-affbb7091de81103538e2740c5b2c762.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/icons/weather/weather_rain.png" +dest_files=["res://.godot/imported/weather_rain.png-affbb7091de81103538e2740c5b2c762.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/ui/interface_font_controller.gd b/ui/interface_font_controller.gd index 239ebbd..f8db71e 100644 --- a/ui/interface_font_controller.gd +++ b/ui/interface_font_controller.gd @@ -2,9 +2,13 @@ class_name InterfaceFontController extends Node const STANDARD_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf") +const COMPACT_FILE_DIALOG_LIMIT := Vector2i(800, 600) +const COMPACT_FILE_DIALOG_MARGIN := Vector2i(12, 12) +const COMPACT_FILE_DIALOG_FONT_SIZE: int = 17 var _game_theme: Theme = preload("res://ui/game_theme.tres") var _utility_theme: Theme +var _compact_file_dialog_theme: Theme func _ready() -> void: @@ -40,5 +44,66 @@ func apply_utility_theme(themed_node: Node) -> void: (themed_node as Window).theme = _utility_theme +func popup_file_dialog(dialog: FileDialog) -> void: + if dialog == null or not dialog.is_inside_tree(): + return + var host_window: Window = dialog.get_parent().get_window() + var host_size: Vector2i = host_window.size + var compact: bool = ( + host_size.x <= COMPACT_FILE_DIALOG_LIMIT.x + or host_size.y <= COMPACT_FILE_DIALOG_LIMIT.y + ) + if not compact: + apply_utility_theme(dialog) + dialog.min_size = Vector2i(200, 70) + dialog.max_size = Vector2i.ZERO + dialog.popup_centered_ratio(0.75) + return + if _compact_file_dialog_theme == null: + _compact_file_dialog_theme = _utility_theme.duplicate(true) + _compact_file_dialog_theme.default_font_size = ( + COMPACT_FILE_DIALOG_FONT_SIZE + ) + dialog.theme = _compact_file_dialog_theme + var available_size := Vector2i( + maxi(1, host_size.x - COMPACT_FILE_DIALOG_MARGIN.x * 2), + maxi(1, host_size.y - COMPACT_FILE_DIALOG_MARGIN.y * 2), + ) + dialog.min_size = Vector2i.ZERO + dialog.max_size = available_size + dialog.popup_centered(available_size) + # FileDialog's desktop-oriented intrinsic minimum can exceed a compact + # 4:3 display. The compact font keeps its controls usable while this final + # clamp guarantees the embedded window cannot escape the physical screen. + dialog.size = available_size + if dialog.get_viewport().gui_embed_subwindows: + dialog.position = (host_size - available_size) / 2 + _finalize_compact_file_dialog.call_deferred( + dialog, host_size, available_size + ) + + +func _finalize_compact_file_dialog( + dialog: FileDialog, + host_size: Vector2i, + requested_size: Vector2i, +) -> void: + await get_tree().process_frame + if dialog == null or not dialog.visible: + return + # Embedded windows report their content origin below the title bar. Account + # for that inset after Godot has laid the window out so its bottom and right + # edges retain the same safe margin as its decorated top and left edges. + var safe_bottom_right: Vector2i = host_size - COMPACT_FILE_DIALOG_MARGIN + var fitted_size: Vector2i = requested_size + fitted_size.x -= maxi( + 0, dialog.position.x + fitted_size.x - safe_bottom_right.x + ) + fitted_size.y -= maxi( + 0, dialog.position.y + fitted_size.y - safe_bottom_right.y + ) + dialog.size = Vector2i(maxi(1, fitted_size.x), maxi(1, fitted_size.y)) + + func readable_font() -> Font: return STANDARD_FONT diff --git a/ui/logbook_catalog.gd b/ui/logbook_catalog.gd index ad1a9c9..1ba1a1e 100644 --- a/ui/logbook_catalog.gd +++ b/ui/logbook_catalog.gd @@ -14,6 +14,51 @@ const CATALOG_ORDER: Array[StringName] = [ &"catfish_channel", &"catfish_flathead", &"catfish_white", + &"tuna_albacore", + &"tuna_bigeye", + &"tuna_bluefin", + &"tuna_skipjack", + &"tuna_yellowfin", + &"goby_round", + &"salmon_atlantic", + &"salmon_chum", + &"salmon_coho", + &"salmon_pink", + &"salmon_sockeye", + &"anchovy_european", + &"anchovy_northern", + &"chub_european", + &"chub_flame", + &"chub_lake", + &"goldfish", + &"goldfish_bubbleeye", + &"grouper_gulf", + &"grouper_red", + &"mackerel_atlantic", + &"mackerel_cero", + &"mackerel_chub", + &"mackerel_king", + &"mackerel_spanish", + &"marlin_black", + &"marlin_blue", + &"marlin_white", + &"pomfret_black", + &"pomfret_chinese", + &"pomfret_golden", + &"pomfret_white", + &"sailfish", + &"sauger", + &"saugeye", + &"snapper_lane", + &"snapper_mangrove", + &"snapper_mutton", + &"snapper_red", + &"swordfish", + &"trout_cutthroat", + &"trout_golden", + &"trout_rainbow", + &"trout_steelhead", + &"walleye", ] # Short presentation notes are kept here with the catalog contract rather @@ -52,6 +97,186 @@ const FISH_FACTS: Dictionary[StringName, String] = { "white catfish began along the atlantic coast between new york " + "and florida. people later carried them far beyond that home range." ), + &"tuna_albacore": ( + "albacore have unusually long pectoral fins. their warm muscles help " + + "them cruise across wide stretches of open ocean." + ), + &"tuna_bigeye": ( + "bigeye tuna make daily trips between deep, cool water and warmer " + + "surface layers. their large eyes suit those dim depths." + ), + &"tuna_bluefin": ( + "bluefin tuna are powerful ocean travelers. heat retained in their " + + "swimming muscles lets them thrive in surprisingly cold seas." + ), + &"tuna_skipjack": ( + "skipjack travel in fast-moving schools and have dark stripes along " + + "their lower sides. they rarely stop swimming." + ), + &"tuna_yellowfin": ( + "yellowfin are named for their bright yellow fins and finlets. large " + + "schools often gather with other open-ocean hunters." + ), + &"goby_round": ( + "round gobies use fused pelvic fins like a suction cup to hold onto " + + "rocks. they are small fish with a famously large appetite." + ), + &"salmon_atlantic": ( + "atlantic salmon can return from the ocean to the river where they " + + "hatched. unlike many salmon, some survive to make the trip again." + ), + &"salmon_chum": ( + "chum salmon travel immense distances and develop striking bars when " + + "they return to fresh water to spawn." + ), + &"salmon_coho": ( + "coho salmon are agile hunters known for powerful leaps. young coho " + + "may spend more than a year growing in streams." + ), + &"salmon_pink": ( + "pink salmon usually follow a strict two-year life cycle. spawning " + + "males develop the hump that inspired their nickname." + ), + &"salmon_sockeye": ( + "sockeye salmon feed heavily on plankton at sea. adults turn vivid red " + + "as they return inland to spawn." + ), + &"anchovy_european": ( + "european anchovies form dense schools and feed by filtering tiny " + + "plankton from coastal water." + ), + &"anchovy_northern": ( + "northern anchovies gather in huge schools along the pacific coast " + + "and filter plankton with their fine gill rakers." + ), + &"chub_european": ( + "european chub are adaptable river fish. adults often patrol " + + "shallows and feed on insects, small fish, and fruit." + ), + &"chub_flame": ( + "flame chubs prefer cool, clear streams. their bright breeding colors " + + "make a small fish easy to spot in spring water." + ), + &"chub_lake": ( + "lake chubs school in cold northern water and use their small mouths " + + "to pick insects and other tiny prey from the current." + ), + &"goldfish": ( + "goldfish can tolerate a surprisingly wide range of conditions. " + + "their wild relatives often live in slow, weedy water." + ), + &"goldfish_bubbleeye": ( + "bubble-eye goldfish have delicate fluid-filled sacs beneath their " + + "eyes, making careful handling especially important." + ), + &"grouper_gulf": ( + "gulf grouper are ambush predators that use a sudden gulp to pull " + + "prey into their cavernous mouths." + ), + &"grouper_red": ( + "red grouper excavate shelters in reef rubble. the hollows they make " + + "can become hiding places for many smaller animals." + ), + &"mackerel_atlantic": ( + "atlantic mackerel travel in fast schools across the north atlantic " + + "and feed on plankton and small schooling fish." + ), + &"mackerel_cero": ( + "cero mackerel are streamlined coastal hunters. sharp teeth help " + + "them slash through schools of smaller fish." + ), + &"mackerel_chub": ( + "chub mackerel school near the surface and follow plankton blooms. " + + "their dark wavy bars help identify them at a glance." + ), + &"mackerel_king": ( + "king mackerel are swift coastal predators. their pointed teeth and " + + "long body are built for sudden bursts of speed." + ), + &"mackerel_spanish": ( + "spanish mackerel hunt in warm coastal waters and often travel in " + + "loose schools while chasing baitfish." + ), + &"marlin_black": ( + "black marlin are powerful billfish that spend much of their lives " + + "in warm open ocean and can make blistering runs." + ), + &"marlin_blue": ( + "blue marlin are highly migratory open-ocean hunters. their long bill " + + "helps them stun prey before turning back to feed." + ), + &"marlin_white": ( + "white marlin favor warm offshore water and use their rounded dorsal " + + "fin and bill to weave through schools of prey." + ), + &"pomfret_black": ( + "black pomfret have a deep, laterally compressed body that lets them " + + "maneuver neatly through open water." + ), + &"pomfret_chinese": ( + "chinese pomfret are silvery schooling fish with a tall, flattened " + + "body and long sickle-shaped fins." + ), + &"pomfret_golden": ( + "golden pomfret are warm-water swimmers whose golden sheen is most " + + "noticeable along the fins and flanks." + ), + &"pomfret_white": ( + "white pomfret gather over coastal grounds and use their compact, " + + "deep bodies to turn quickly while feeding." + ), + &"sailfish": ( + "sailfish raise their enormous dorsal fin while herding baitfish. " + + "they are among the fastest fish in the sea." + ), + &"sauger": ( + "sauger favor turbid rivers and reservoirs. their mottled backs " + + "blend into the dim, shifting light near the bottom." + ), + &"saugeye": ( + "saugeye are a fertile hybrid of sauger and walleye. they combine " + + "traits from both parent species and often grow quickly." + ), + &"snapper_lane": ( + "lane snapper use reef structure for cover and hunt small crustaceans " + + "and fish when the light begins to fade." + ), + &"snapper_mangrove": ( + "mangrove snapper shelter among roots and docks when young, then " + + "move toward reefs as they grow." + ), + &"snapper_mutton": ( + "mutton snapper have canine teeth that help them pick crabs, shrimp, " + + "and other hard-shelled prey from the reef." + ), + &"snapper_red": ( + "red snapper gather around reefs, wrecks, and ledges. their diet " + + "includes small fish, shrimp, and squid." + ), + &"swordfish": ( + "swordfish are fast, wide-ranging predators with a flattened bill. " + + "they can cross deep water and warm surface layers in one day." + ), + &"trout_cutthroat": ( + "cutthroat trout take their name from the red slash beneath the jaw. " + + "many populations rely on cold, well-oxygenated streams." + ), + &"trout_golden": ( + "golden trout evolved in clear, high-elevation california streams. " + + "their bright flanks stand out against rocky alpine water." + ), + &"trout_rainbow": ( + "rainbow trout are adaptable stream hunters. the pink lateral stripe " + + "is especially vivid when the fish is in breeding condition." + ), + &"trout_steelhead": ( + "steelhead are the ocean-going form of rainbow trout. adults can " + + "leave fresh water and later return to spawn." + ), + &"walleye": ( + "walleye have light-sensitive eyes that help them hunt in murky water " + + "and around dusk. their reflective layer gives the eyes a pale glow." + ), } static func category_for(fish: FishDataType) -> WaterType.Type: diff --git a/ui/logbook_page.gd b/ui/logbook_page.gd index 115b791..ee63797 100644 --- a/ui/logbook_page.gd +++ b/ui/logbook_page.gd @@ -16,6 +16,9 @@ const InventoryNotepadType = preload( const LogbookPortraitType = preload( "res://ui/components/logbook_portrait.gd" ) +const CurrencyPresentationType = preload( + "res://ui/currency_presentation.gd" +) const DETAIL_FADE_DURATION: float = 0.12 const CATEGORY_FADE_DURATION: float = UIMotion.UTILITY_EXIT_DURATION @@ -23,9 +26,26 @@ const HANDWRITTEN_NUMERIC_SCALE: float = 0.8 const PORTRAIT_VIEW_MAX_SIZE := Vector2(860.0, 480.0) const INK := Color("251b10") const MUTED_INK := Color("6d5b45") -const PAPER := Color("f2e6c9") -const PAPER_DARK := Color("e8d7b4") -const LOGBOOK_TAB_LEFT_INSET: float = 34.0 +const LOGBOOK_ARTWORK: Texture2D = preload( + "res://ui/assets/logbook/ui_logbook.png" +) +const SCROLL_UP_TEXTURE: Texture2D = preload( + "res://ui/icons/pictograms/arrow_dark_up_full.png" +) +const SCROLL_DOWN_TEXTURE: Texture2D = preload( + "res://ui/icons/pictograms/arrow_dark_down_full.png" +) +const LOGBOOK_ARTWORK_SOURCE_SIZE := Vector2(512.0, 247.0) +const LEFT_PAGE_CONTENT_RECT := Rect2(56.0, 26.0, 190.0, 198.0) +const RIGHT_PAGE_CONTENT_RECT := Rect2(267.0, 26.0, 210.0, 198.0) +const LOGBOOK_TAB_LEFT_INSET: float = 122.0 +const PAGE_CONTENT_SCALE: float = 0.97 +const CATALOG_PORTRAIT_SIZE := Vector2(78.0, 36.0) +const CATALOG_ENTRY_SIZE := Vector2(92.0, 92.0) +const CATALOG_ROW_STEP: float = 98.0 +const CATALOG_SNAP_DELAY: float = 0.12 +const DETAIL_PORTRAIT_SIZE := Vector2(160.0, 88.0) +const DETAIL_BOTTOM_INSET: float = 35.0 var _collection_log: CollectionLogType var _inventory: FishInventoryType @@ -41,10 +61,14 @@ var _detail_tween: Tween var _category_generation: int = 0 var _category_tween: Tween var _silhouette_material: ShaderMaterial +var _snapping_catalog_scroll: bool = false +var _catalog_scroll_snap_timer: Timer var _category_tabs: Array[Button] = [] var _catalog_scroll: ScrollContainer var _catalog_grid: GridContainer +var _catalog_scroll_up_indicator: TextureRect +var _catalog_scroll_down_indicator: TextureRect var _empty_state: Label var _detail_body: VBoxContainer var _detail_portrait_button: Button @@ -145,7 +169,7 @@ func _build_interface() -> void: var outer := MarginContainer.new() outer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) outer.add_theme_constant_override("margin_left", 52) - outer.add_theme_constant_override("margin_top", 116) + outer.add_theme_constant_override("margin_top", 85) outer.add_theme_constant_override("margin_right", 52) outer.add_theme_constant_override("margin_bottom", 18) add_child(outer) @@ -155,9 +179,8 @@ func _build_interface() -> void: outer.add_child(stack) var tabs := HBoxContainer.new() - # The 26 px page corner plus 8 px clearance keeps the flange behind - # the straight portion of the spread. - tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 20.0) + # Align the first flange with the authored left paper edge. + tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 37.0) tabs.size = Vector2(384.0, 38.0) tabs.z_index = 10 tabs.mouse_filter = Control.MOUSE_FILTER_PASS @@ -179,71 +202,206 @@ func _build_interface() -> void: _category_tabs.append(tab) _configure_category_focus() - var book := HBoxContainer.new() + var book := Control.new() book.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) book.offset_top = 50.0 book.z_index = 20 - book.add_theme_constant_override("separation", 12) + book.mouse_filter = Control.MOUSE_FILTER_IGNORE stack.add_child(book) - var left_page := PanelContainer.new() - left_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL - left_page.size_flags_stretch_ratio = 1.12 - left_page.add_theme_stylebox_override( - "panel", _paper_style(PAPER, true) - ) + var artwork := TextureRect.new() + artwork.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + artwork.texture = LOGBOOK_ARTWORK + artwork.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + artwork.stretch_mode = TextureRect.STRETCH_SCALE + artwork.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE + book.add_child(artwork) + + var left_page := Control.new() + left_page.mouse_filter = Control.MOUSE_FILTER_IGNORE + _apply_artwork_rect(left_page, LEFT_PAGE_CONTENT_RECT) book.add_child(left_page) - var left_margin := MarginContainer.new() - _set_margins(left_margin, 18, 16, 18, 16) - left_page.add_child(left_margin) var left_layout := VBoxContainer.new() + left_layout.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) left_layout.add_theme_constant_override("separation", 8) - left_margin.add_child(left_layout) - var heading := _label("catch catalog", 25) - heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - left_layout.add_child(heading) + left_page.add_child(left_layout) + _apply_page_content_scale(left_layout) + var scroll_indicator_top_lane := Control.new() + scroll_indicator_top_lane.custom_minimum_size.y = 20.0 + scroll_indicator_top_lane.mouse_filter = Control.MOUSE_FILTER_IGNORE + left_layout.add_child(scroll_indicator_top_lane) _empty_state = _label("", 18) _empty_state.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _empty_state.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _empty_state.size_flags_vertical = Control.SIZE_EXPAND_FILL left_layout.add_child(_empty_state) + var catalog_scroll_margin := MarginContainer.new() + catalog_scroll_margin.size_flags_vertical = Control.SIZE_EXPAND_FILL + catalog_scroll_margin.add_theme_constant_override("margin_left", 20) + left_layout.add_child(catalog_scroll_margin) _catalog_scroll = ScrollContainer.new() _catalog_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL _catalog_scroll.horizontal_scroll_mode = ( ScrollContainer.SCROLL_MODE_DISABLED ) - left_layout.add_child(_catalog_scroll) + _catalog_scroll.scroll_vertical_custom_step = CATALOG_ROW_STEP + catalog_scroll_margin.add_child(_catalog_scroll) _catalog_grid = GridContainer.new() - _catalog_grid.columns = 5 + _catalog_grid.columns = 4 _catalog_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _catalog_grid.add_theme_constant_override("h_separation", 8) - _catalog_grid.add_theme_constant_override("v_separation", 8) + _catalog_grid.add_theme_constant_override("h_separation", 6) + _catalog_grid.add_theme_constant_override("v_separation", 6) _catalog_scroll.add_child(_catalog_grid) - - var gutter := ColorRect.new() - gutter.custom_minimum_size.x = 10 - gutter.color = Color("795f3f") - gutter.mouse_filter = Control.MOUSE_FILTER_IGNORE - book.add_child(gutter) - - var right_page := PanelContainer.new() - right_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL - right_page.add_theme_stylebox_override( - "panel", _paper_style(PAPER_DARK, false) + var scroll_indicator_bottom_lane := Control.new() + scroll_indicator_bottom_lane.custom_minimum_size.y = 32.0 + scroll_indicator_bottom_lane.mouse_filter = Control.MOUSE_FILTER_IGNORE + left_layout.add_child(scroll_indicator_bottom_lane) + _configure_catalog_scroll_bar() + _catalog_scroll_up_indicator = _make_scroll_indicator( + "CatalogScrollUpIndicator", + SCROLL_UP_TEXTURE, + true, ) + left_page.add_child(_catalog_scroll_up_indicator) + _catalog_scroll_down_indicator = _make_scroll_indicator( + "CatalogScrollDownIndicator", + SCROLL_DOWN_TEXTURE, + false, + ) + left_page.add_child(_catalog_scroll_down_indicator) + + var right_page := Control.new() + right_page.mouse_filter = Control.MOUSE_FILTER_IGNORE + _apply_artwork_rect(right_page, RIGHT_PAGE_CONTENT_RECT) book.add_child(right_page) - var right_margin := MarginContainer.new() - _set_margins(right_margin, 22, 16, 22, 16) - right_page.add_child(right_margin) _detail_body = VBoxContainer.new() + _detail_body.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _detail_body.size_flags_horizontal = Control.SIZE_EXPAND_FILL _detail_body.size_flags_vertical = Control.SIZE_EXPAND_FILL _detail_body.add_theme_constant_override("separation", 16) - right_margin.add_child(_detail_body) + right_page.add_child(_detail_body) + _apply_page_content_scale(_detail_body) _show_no_selection() _build_portrait_overlay() +func _apply_artwork_rect(control: Control, source_rect: Rect2) -> void: + control.anchor_left = source_rect.position.x / LOGBOOK_ARTWORK_SOURCE_SIZE.x + control.anchor_top = source_rect.position.y / LOGBOOK_ARTWORK_SOURCE_SIZE.y + control.anchor_right = source_rect.end.x / LOGBOOK_ARTWORK_SOURCE_SIZE.x + control.anchor_bottom = source_rect.end.y / LOGBOOK_ARTWORK_SOURCE_SIZE.y + control.offset_left = 0.0 + control.offset_top = 0.0 + control.offset_right = 0.0 + control.offset_bottom = 0.0 + + +func _apply_page_content_scale(control: Control) -> void: + control.scale = Vector2.ONE * PAGE_CONTENT_SCALE + control.resized.connect(_center_scaled_content.bind(control)) + _center_scaled_content.call_deferred(control) + + +func _center_scaled_content(control: Control) -> void: + if is_instance_valid(control): + control.pivot_offset = control.size * 0.5 + + +func _configure_catalog_scroll_bar() -> void: + _catalog_scroll_snap_timer = Timer.new() + _catalog_scroll_snap_timer.one_shot = true + _catalog_scroll_snap_timer.wait_time = CATALOG_SNAP_DELAY + _catalog_scroll_snap_timer.timeout.connect(_snap_catalog_scroll_to_row) + _catalog_scroll.add_child(_catalog_scroll_snap_timer) + var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar() + scroll_bar.mouse_filter = Control.MOUSE_FILTER_IGNORE + scroll_bar.focus_mode = Control.FOCUS_NONE + scroll_bar.self_modulate = Color.TRANSPARENT + scroll_bar.custom_minimum_size.x = 0.0 + scroll_bar.add_theme_constant_override("scroll_size", 0) + var empty_style := StyleBoxEmpty.new() + for style_name: StringName in [ + &"scroll", + &"scroll_focus", + &"grabber", + &"grabber_highlight", + &"grabber_pressed", + ]: + scroll_bar.add_theme_stylebox_override(style_name, empty_style) + scroll_bar.changed.connect(_refresh_catalog_scroll_indicators) + scroll_bar.value_changed.connect(_on_catalog_scroll_value_changed) + + +func _make_scroll_indicator( + indicator_name: String, + indicator_texture: Texture2D, + at_top: bool, +) -> TextureRect: + var indicator := TextureRect.new() + indicator.name = indicator_name + indicator.anchor_left = 0.5 + indicator.anchor_right = 0.5 + indicator.anchor_top = 0.0 if at_top else 1.0 + indicator.anchor_bottom = indicator.anchor_top + indicator.offset_left = -14.0 + indicator.offset_right = 14.0 + indicator.offset_top = -8.0 if at_top else -35.0 + indicator.offset_bottom = 20.0 if at_top else -7.0 + indicator.z_index = 30 + indicator.texture = indicator_texture + indicator.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + indicator.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + indicator.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + indicator.mouse_filter = Control.MOUSE_FILTER_IGNORE + indicator.visible = false + return indicator + + +func _on_catalog_scroll_value_changed(_value: float) -> void: + if _snapping_catalog_scroll: + return + _catalog_scroll_snap_timer.start() + _refresh_catalog_scroll_indicators() + + +func _snap_catalog_scroll_to_row() -> void: + var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar() + var value: float = scroll_bar.value + var maximum_scroll: float = scroll_bar.max_value - scroll_bar.page + var snapped_value: float = clampf( + roundf(value / CATALOG_ROW_STEP) * CATALOG_ROW_STEP, + scroll_bar.min_value, + maximum_scroll, + ) + if not is_equal_approx(value, snapped_value): + _snapping_catalog_scroll = true + _catalog_scroll.scroll_vertical = roundi(snapped_value) + _snapping_catalog_scroll = false + _refresh_catalog_scroll_indicators() + + +func _refresh_catalog_scroll_indicators() -> void: + if ( + _catalog_scroll == null + or _catalog_scroll_up_indicator == null + or _catalog_scroll_down_indicator == null + ): + return + var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar() + var maximum_scroll: float = scroll_bar.max_value - scroll_bar.page + var has_overflow: bool = ( + _catalog_scroll.visible + and maximum_scroll > scroll_bar.min_value + 0.5 + ) + _catalog_scroll_up_indicator.visible = ( + has_overflow and scroll_bar.value > scroll_bar.min_value + 0.5 + ) + _catalog_scroll_down_indicator.visible = ( + has_overflow and scroll_bar.value < maximum_scroll - 0.5 + ) + + func _refresh_catalog() -> void: if not is_node_ready(): return @@ -293,11 +451,13 @@ func _refresh_catalog() -> void: _refresh_selection_styles() _refresh_details(false) set_interactive(_interactive) + _refresh_catalog_scroll_indicators.call_deferred() func _make_entry(fish: FishDataType, discovered: bool) -> Button: var entry := Button.new() - entry.custom_minimum_size = Vector2(102, 102) + entry.set_meta(&"controller_focus_inversion_disabled", true) + entry.custom_minimum_size = CATALOG_ENTRY_SIZE entry.size_flags_horizontal = Control.SIZE_SHRINK_CENTER entry.size_flags_vertical = Control.SIZE_SHRINK_CENTER entry.toggle_mode = true @@ -344,7 +504,7 @@ func _add_entry_content( var portrait_view := LogbookPortraitType.new() portrait_view.configure( portrait, - LogbookPortraitType.ENTRY_FRAME_SIZE, + CATALOG_PORTRAIT_SIZE, _silhouette_material if unknown else null, ) content.add_child(portrait_view) @@ -495,10 +655,14 @@ func _refresh_details(animate: bool) -> void: func _build_known_details(fish: FishDataType) -> void: _clear_details() var catalog_number: int = LogbookCatalog.catalog_number(fish.id) + var summary_margin := MarginContainer.new() + summary_margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _set_margins(summary_margin, 40, 0, 40, 0) + _detail_body.add_child(summary_margin) var summary_columns := HBoxContainer.new() summary_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL summary_columns.add_theme_constant_override("separation", 24) - _detail_body.add_child(summary_columns) + summary_margin.add_child(summary_columns) var portrait_column := VBoxContainer.new() portrait_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL @@ -510,12 +674,11 @@ func _build_known_details(fish: FishDataType) -> void: portrait_column.add_child(title) if fish.display_texture != null: _detail_portrait_button = Button.new() - _detail_portrait_button.custom_minimum_size = ( - LogbookPortraitType.DETAIL_FRAME_SIZE - ) + _detail_portrait_button.custom_minimum_size = DETAIL_PORTRAIT_SIZE _detail_portrait_button.size_flags_horizontal = ( Control.SIZE_SHRINK_CENTER ) + _detail_portrait_button.clip_contents = true _detail_portrait_button.tooltip_text = "View larger artwork" _detail_portrait_button.accessibility_name = ( "View larger artwork for %s" % fish.display_name @@ -545,7 +708,7 @@ func _build_known_details(fish: FishDataType) -> void: var artwork := LogbookPortraitType.new() artwork.configure( fish.display_texture, - LogbookPortraitType.DETAIL_FRAME_SIZE, + DETAIL_PORTRAIT_SIZE, ) artwork_center.add_child(artwork) @@ -562,11 +725,15 @@ func _build_known_details(fish: FishDataType) -> void: facts_column.add_child(facts) _detail_body.add_child(_build_quality_progress(fish.id)) + var stats_anchor := VBoxContainer.new() + stats_anchor.size_flags_horizontal = Control.SIZE_EXPAND_FILL + stats_anchor.size_flags_vertical = Control.SIZE_EXPAND_FILL + stats_anchor.alignment = BoxContainer.ALIGNMENT_END + _detail_body.add_child(stats_anchor) var stats_columns := HBoxContainer.new() stats_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL - stats_columns.size_flags_vertical = Control.SIZE_EXPAND_FILL stats_columns.add_theme_constant_override("separation", 28) - _detail_body.add_child(stats_columns) + stats_anchor.add_child(stats_columns) var left_stats := _make_stats_column() var right_stats := _make_stats_column() stats_columns.add_child(left_stats) @@ -592,30 +759,33 @@ func _build_known_details(fish: FishDataType) -> void: _add_detail_row(left_stats, "number caught", "unknown") _add_detail_row(right_stats, "rarity", fish.get_rarity_name().to_lower()) _add_detail_row(right_stats, "time of day", _availability_text(fish)) - _add_detail_row( + _add_currency_detail_row( right_stats, "value range", - "%d–%d fish coin" % [ - FishQualityType.apply_sale_value( - fish.sell_value_min, - FishQualityType.Tier.BORING, - ), - FishQualityType.apply_sale_value( - fish.sell_value_max, - FishQualityType.Tier.SHINY, - ), - ], + FishQualityType.apply_sale_value( + fish.sell_value_min, + FishQualityType.Tier.BORING, + ), + FishQualityType.apply_sale_value( + fish.sell_value_max, + FishQualityType.Tier.SHINY, + ), ) _add_detail_row( right_stats, "number owned", str(_inventory.get_count(fish.id) if _inventory != null else 0), ) + var bottom_inset := Control.new() + bottom_inset.custom_minimum_size.y = DETAIL_BOTTOM_INSET + bottom_inset.mouse_filter = Control.MOUSE_FILTER_IGNORE + stats_anchor.add_child(bottom_inset) func _build_quality_progress(fish_id: StringName) -> VBoxContainer: var quality_section := VBoxContainer.new() - quality_section.add_theme_constant_override("separation", 3) + quality_section.size_flags_horizontal = Control.SIZE_EXPAND_FILL + quality_section.add_theme_constant_override("separation", 6) var discovered_count: int = 0 for quality: int in FishQualityType.TIER_COUNT: if _collection_log.has_discovered_quality(fish_id, quality): @@ -625,29 +795,36 @@ func _build_quality_progress(fish_id: StringName) -> VBoxContainer: discovered_count, FishQualityType.TIER_COUNT, ], - 14, + 17, ) heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER heading.add_theme_color_override("font_color", MUTED_INK) quality_section.add_child(heading) - var tiers := HBoxContainer.new() - tiers.alignment = BoxContainer.ALIGNMENT_CENTER - tiers.add_theme_constant_override("separation", 10) + var tiers := VBoxContainer.new() + tiers.add_theme_constant_override("separation", 6) quality_section.add_child(tiers) for quality: int in FishQualityType.TIER_COUNT: + var row: HBoxContainer + if quality % 2 == 0: + row = HBoxContainer.new() + row.alignment = BoxContainer.ALIGNMENT_CENTER + row.add_theme_constant_override("separation", 28) + tiers.add_child(row) + else: + row = tiers.get_child(tiers.get_child_count() - 1) as HBoxContainer var discovered: bool = _collection_log.has_discovered_quality( fish_id, quality, ) var tier := HBoxContainer.new() tier.alignment = BoxContainer.ALIGNMENT_CENTER - tier.add_theme_constant_override("separation", 3) + tier.add_theme_constant_override("separation", 5) var quality_color: Color = UIPalette.get_quality_color(quality) - var dot := _label("●" if discovered else "○", 13) + var dot := _label("●" if discovered else "○", 17) dot.add_theme_color_override("font_color", quality_color) dot.modulate.a = 1.0 if discovered else 0.48 tier.add_child(dot) - var tier_label := _label(FishQualityType.display_name(quality), 13) + var tier_label := _label(FishQualityType.display_name(quality), 16) tier_label.add_theme_color_override( "font_color", INK if discovered else MUTED_INK, @@ -659,7 +836,7 @@ func _build_quality_progress(fish_id: StringName) -> VBoxContainer: else "%s quality not yet collected" ) % FishQualityType.display_name(quality) tier.add_child(tier_label) - tiers.add_child(tier) + row.add_child(tier) return quality_section @@ -702,6 +879,31 @@ func _add_detail_row( parent.add_child(row) +func _add_currency_detail_row( + parent: VBoxContainer, + row_name: String, + minimum_value: int, + maximum_value: int, +) -> void: + var row := VBoxContainer.new() + row.add_theme_constant_override("separation", 2) + var heading := _field_label(row_name, 14) + heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + heading.add_theme_color_override("font_color", MUTED_INK) + row.add_child(heading) + var value: CurrencyAmount = ( + CurrencyPresentationType.instantiate_amount(minimum_value, 16.0) + ) + value.set_amount_text("%d–%d" % [minimum_value, maximum_value]) + value.get_amount_label().add_theme_font_override( + "font", InventoryNotepadType.HANDWRITTEN_FONT + ) + value.get_amount_label().add_theme_font_size_override("font_size", 16) + value.get_amount_label().add_theme_color_override("font_color", INK) + row.add_child(value) + parent.add_child(row) + + func _make_stats_column() -> VBoxContainer: var column := VBoxContainer.new() column.size_flags_horizontal = Control.SIZE_EXPAND_FILL @@ -848,18 +1050,6 @@ func _entry_label_text(entry_name: String) -> String: ) -func _paper_style(color: Color, left: bool) -> StyleBoxFlat: - var style := StyleBoxFlat.new() - style.bg_color = color - style.border_color = Color("9b7a4f") - style.set_border_width_all(3) - style.corner_radius_top_left = 26 if left else 6 - style.corner_radius_bottom_left = 32 if left else 6 - style.corner_radius_top_right = 6 if left else 28 - style.corner_radius_bottom_right = 6 if left else 34 - return style - - func _build_portrait_overlay() -> void: _portrait_overlay = Control.new() _portrait_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) diff --git a/ui/mail_page.gd b/ui/mail_page.gd index 746835a..f1bd37d 100644 --- a/ui/mail_page.gd +++ b/ui/mail_page.gd @@ -1,8 +1,6 @@ class_name MailPage extends Control -signal focus_requested - const GREETING_LABELS := { "dear": "Dear", "to": "To", @@ -17,6 +15,9 @@ const SALUTATION_LABELS := { "good_luck_have_fun": "Good Luck Have Fun", } const FishQualityType = preload("res://fish/fish_quality.gd") +const CurrencyPresentationType = preload( + "res://ui/currency_presentation.gd" +) var _service: NetworkMailService var _reservations: PlayerAssetReservationService @@ -38,14 +39,16 @@ var _signature: Label var _attachment_kind: OptionButton var _attachment_choice: OptionButton var _attachment_amount: LineEdit -var _coin_available: Label +var _coin_available_heading: Label +var _coin_available: CurrencyAmount +var _attachment_amount_currency_icon: TextureRect var _amount_minus: Button var _amount_plus: Button -var _attachment_summary: Label +var _attachment_summary: RichTextLabel var _send_button: Button var _status: Label var _letter_text: Label -var _letter_gift: Label +var _letter_gift: RichTextLabel var _accept: Button var _decline: Button var _archive: Button @@ -113,17 +116,7 @@ func set_interactive(value: bool) -> void: func _build_ui() -> void: - var paper := PanelContainer.new() - paper.position = Vector2(116, 126) - paper.size = Vector2(1048, 540) - paper.add_theme_stylebox_override("panel", UtilityPageStyle.panel_style()) - add_child(paper) - var margin := MarginContainer.new() - margin.add_theme_constant_override("margin_left", 34) - margin.add_theme_constant_override("margin_right", 34) - margin.add_theme_constant_override("margin_top", 28) - margin.add_theme_constant_override("margin_bottom", 28) - paper.add_child(margin) + var margin: MarginContainer = UtilityPageStyle.build_laptop_screen(self) var root := Control.new() margin.add_child(root) _inbox = _build_inbox() @@ -133,8 +126,8 @@ func _build_ui() -> void: page.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) root.add_child(page) _status = Label.new() - _status.position = Vector2(18, 448) - _status.size = Vector2(900, 28) + _status.position = Vector2(18, 408) + _status.size = Vector2(1024, 28) _status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _status.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY @@ -147,7 +140,7 @@ func _build_ui() -> void: func _build_inbox() -> Control: var page := Control.new() var title := Label.new() - title.text = "Mail" + title.text = "mail" title.position = Vector2(12, 0) title.size = Vector2(500, 42) title.add_theme_font_size_override("font_size", 30) @@ -170,16 +163,16 @@ func _build_inbox() -> Control: page.add_child(archive_view) var scroll := ScrollContainer.new() scroll.position = Vector2(12, 62) - scroll.size = Vector2(958, 374) + scroll.size = Vector2(1036, 334) page.add_child(scroll) _inbox_list = VBoxContainer.new() - _inbox_list.custom_minimum_size = Vector2(930, 0) + _inbox_list.custom_minimum_size = Vector2(1008, 0) _inbox_list.add_theme_constant_override("separation", 8) scroll.add_child(_inbox_list) _empty_label = Label.new() _empty_label.text = "No letters yet." _empty_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _empty_label.custom_minimum_size = Vector2(930, 80) + _empty_label.custom_minimum_size = Vector2(1008, 80) _inbox_list.add_child(_empty_label) return page @@ -224,7 +217,7 @@ func _build_compose() -> Control: _attachment_kind = OptionButton.new() _attachment_kind.position = Vector2(688, 48) _attachment_kind.size = Vector2(280, 46) - for label: String in ["No gift", "Fish coin", "Fish", "Consumable"]: + for label: String in ["No gift", "Currency", "Fish", "Consumable"]: _attachment_kind.add_item(label) _attachment_kind.item_selected.connect(_refresh_attachment_choices) page.add_child(_attachment_kind) @@ -237,13 +230,34 @@ func _build_compose() -> Control: _update_attachment_summary() ) page.add_child(_attachment_choice) - _coin_available = Label.new() - _coin_available.position = Vector2(688, 158) - _coin_available.size = Vector2(280, 28) + _coin_available_heading = Label.new() + _coin_available_heading.text = "Available" + _coin_available_heading.position = Vector2(688, 158) + _coin_available_heading.size = Vector2(80, 28) + page.add_child(_coin_available_heading) + _coin_available = CurrencyPresentationType.instantiate_amount(0, 18.0) + _coin_available.position = Vector2(770, 158) + _coin_available.size = Vector2(198, 28) + _coin_available.alignment = BoxContainer.ALIGNMENT_BEGIN page.add_child(_coin_available) + _attachment_amount_currency_icon = TextureRect.new() + _attachment_amount_currency_icon.position = Vector2(746, 204) + _attachment_amount_currency_icon.size = Vector2(18, 18) + _attachment_amount_currency_icon.texture = preload( + "res://items/icons/shop/32_currency.png" + ) + _attachment_amount_currency_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + _attachment_amount_currency_icon.stretch_mode = ( + TextureRect.STRETCH_KEEP_ASPECT_CENTERED + ) + _attachment_amount_currency_icon.texture_filter = ( + CanvasItem.TEXTURE_FILTER_NEAREST + ) + _attachment_amount_currency_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE + page.add_child(_attachment_amount_currency_icon) _attachment_amount = LineEdit.new() - _attachment_amount.position = Vector2(746, 190) - _attachment_amount.size = Vector2(164, 46) + _attachment_amount.position = Vector2(770, 190) + _attachment_amount.size = Vector2(140, 46) _attachment_amount.placeholder_text = "0" _attachment_amount.text = "0" _attachment_amount.text_changed.connect(_on_attachment_amount_changed) @@ -260,9 +274,12 @@ func _build_compose() -> Control: _amount_plus.size = Vector2(48, 46) _amount_plus.pressed.connect(_step_attachment_amount.bind(1)) page.add_child(_amount_plus) - _attachment_summary = Label.new() + _attachment_summary = RichTextLabel.new() _attachment_summary.position = Vector2(688, 246) _attachment_summary.size = Vector2(280, 78) + _attachment_summary.bbcode_enabled = true + _attachment_summary.fit_content = true + _attachment_summary.scroll_active = false _attachment_summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART page.add_child(_attachment_summary) var cancel := Button.new() @@ -293,9 +310,12 @@ func _build_letter() -> Control: _letter_text.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _letter_text.vertical_alignment = VERTICAL_ALIGNMENT_TOP page.add_child(_letter_text) - _letter_gift = Label.new() + _letter_gift = RichTextLabel.new() _letter_gift.position = Vector2(730, 46) _letter_gift.size = Vector2(230, 180) + _letter_gift.bbcode_enabled = true + _letter_gift.fit_content = true + _letter_gift.scroll_active = false _letter_gift.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART page.add_child(_letter_gift) _accept = Button.new() @@ -422,7 +442,7 @@ func _refresh_inbox() -> void: empty.text = ( "No archived letters." if _showing_archive else "No letters yet." ) - empty.custom_minimum_size = Vector2(930, 80) + empty.custom_minimum_size = Vector2(1008, 80) empty.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _inbox_list.add_child(empty) return @@ -449,7 +469,7 @@ func _refresh_inbox() -> void: first_line.left(72), " · gift enclosed" if gift else "", ] - button.custom_minimum_size = Vector2(930, 54) + button.custom_minimum_size = Vector2(1008, 54) button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.pressed.connect(_open_letter.bind(str(letter["mail_id"]))) UtilityPageStyle.apply_ocean_button(button) @@ -491,13 +511,18 @@ func _refresh_attachment_choices(_index: int) -> void: _attachment_amount.visible = amount_visible _amount_minus.visible = amount_visible _amount_plus.visible = amount_visible - _coin_available.visible = _attachment_kind.selected == 1 + var currency_selected: bool = _attachment_kind.selected == 1 + _coin_available_heading.visible = currency_selected + _coin_available.visible = currency_selected + _attachment_amount_currency_icon.visible = currency_selected + _attachment_amount.position.x = 770.0 if currency_selected else 746.0 + _attachment_amount.size.x = 140.0 if currency_selected else 164.0 match _attachment_kind.selected: 0: _attachment_choice.add_item("No attachment") 1: - _attachment_choice.add_item("Fish coin") - _coin_available.text = "Available: %d fish coin" % ( + _attachment_choice.add_item("Currency") + _coin_available.set_amount( _reservations.get_available_fish_coin() ) 2: @@ -717,7 +742,9 @@ func _attachment_text(attachment: Dictionary) -> String: PlayerAssetReservationService.AttachmentType.NONE: return "No gift attached." PlayerAssetReservationService.AttachmentType.FISH_COIN: - return "%d fish coin" % int(attachment.get("amount", 0)) + return CurrencyPresentationType.bbcode_amount( + int(attachment.get("amount", 0)), 18 + ) PlayerAssetReservationService.AttachmentType.FISH: var fish_catch := _inventory.get_catch_by_id( StringName(str(attachment.get("catch_id", ""))) diff --git a/ui/network/join_game_page.gd b/ui/network/join_game_page.gd index 81dec27..ca3fbe5 100644 --- a/ui/network/join_game_page.gd +++ b/ui/network/join_game_page.gd @@ -5,13 +5,17 @@ signal join_requested(endpoint: String) signal back_requested enum Mode { + DISCOVER, DIRECT, SAVED, RECENT, } +const DISCOVERY_REFRESH_SECONDS: float = 8.0 + const ADDRESS_FORMAT_HELP: String = ( - "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted." + "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted; " + + "include the port shown by a host when it differs." ) const DIRECT_WORKFLOW_HELP: String = ( "%s\nJoin Now connects once; Save Server stores this address locally." @@ -26,17 +30,18 @@ const DIRECT_WORKFLOW_HELP: String = ( @onready var _name_helper: Label = %NameHelper @onready var _server_list: ItemList = %ServerList @onready var _details: Label = %Details +@onready var _discover_button: Button = %DiscoverButton @onready var _direct_button: Button = %DirectButton @onready var _saved_button: Button = %SavedButton @onready var _recent_button: Button = %RecentButton @onready var _join_button: Button = %JoinButton +@onready var _refresh_button: Button = %RefreshButton @onready var _save_button: Button = %SaveButton @onready var _edit_button: Button = %EditButton @onready var _favorite_button: Button = %FavoriteButton @onready var _delete_button: Button = %DeleteButton @onready var _cancel_button: Button = %CancelButton @onready var _back_button: Button = %BackButton -@onready var _open_close_button: Button = %OpenCloseButton @onready var _status: Label = %Status @onready var _session_summary: Label = %SessionSummary @onready var _delete_confirmation: BubbleConfirmationPage = ( @@ -46,13 +51,18 @@ const DIRECT_WORKFLOW_HELP: String = ( var _network_session: NetworkSession var _saved_servers: SavedServerStore var _server_trust: ServerTrustStore +var _discovery: DiscoveryClient var _gameplay_context: bool = false -var _mode: Mode = Mode.DIRECT +var _mode: Mode = Mode.DISCOVER var _visible_entries: Array[SavedServerEntry] = [] var _selected_entry: SavedServerEntry +var _discovery_rooms: Array[Dictionary] = [] +var _selected_discovery_index: int = -1 +var _discovery_refresh_timer: Timer var _editing_entry_id: String = "" var _name_entry_active: bool = false var _delete_armed: bool = false +var _connection_error_latched: bool = false func _ready() -> void: @@ -62,16 +72,19 @@ func _ready() -> void: "panel", UtilityPageStyle.panel_style() ) for button: BaseButton in [ - _direct_button, _saved_button, _recent_button, _join_button, + _discover_button, _direct_button, _saved_button, _recent_button, + _refresh_button, _join_button, _save_button, _edit_button, _favorite_button, _delete_button, - _cancel_button, _back_button, _open_close_button, + _cancel_button, _back_button, ]: UtilityPageStyle.apply_ocean_button(button) UtilityPageStyle.apply_ocean_line_edit(_address) UtilityPageStyle.apply_ocean_line_edit(_name_edit) + _discover_button.pressed.connect(_set_mode.bind(Mode.DISCOVER)) _direct_button.pressed.connect(_set_mode.bind(Mode.DIRECT)) _saved_button.pressed.connect(_set_mode.bind(Mode.SAVED)) _recent_button.pressed.connect(_set_mode.bind(Mode.RECENT)) + _refresh_button.pressed.connect(_request_discovery_refresh) _join_button.pressed.connect(_request_join) _save_button.pressed.connect(_on_save_pressed) _edit_button.pressed.connect(_on_edit_pressed) @@ -79,7 +92,6 @@ func _ready() -> void: _delete_button.pressed.connect(_on_delete_pressed) _cancel_button.pressed.connect(_on_cancel_pressed) _back_button.pressed.connect(_on_back_pressed) - _open_close_button.pressed.connect(_on_open_close_pressed) _address.text_submitted.connect(func(_value: String) -> void: if _name_entry_active: _name_edit.grab_focus() @@ -96,6 +108,11 @@ func _ready() -> void: ) _delete_confirmation.confirmed.connect(_confirm_delete) _delete_confirmation.cancelled.connect(_cancel_delete) + _discovery_refresh_timer = Timer.new() + _discovery_refresh_timer.wait_time = DISCOVERY_REFRESH_SECONDS + _discovery_refresh_timer.one_shot = false + _discovery_refresh_timer.timeout.connect(_request_discovery_refresh) + add_child(_discovery_refresh_timer) hide() @@ -104,11 +121,13 @@ func setup( saved_servers: SavedServerStore, gameplay_context: bool, server_trust: ServerTrustStore = null, + discovery: DiscoveryClient = null, ) -> void: _network_session = network_session _saved_servers = saved_servers _gameplay_context = gameplay_context _server_trust = server_trust + _discovery = discovery if not _network_session.state_changed.is_connected(_on_state_changed): _network_session.state_changed.connect(_on_state_changed) if not _network_session.status_message_changed.is_connected( @@ -130,6 +149,29 @@ func setup( and not _saved_servers.data_changed.is_connected(_on_store_changed) ): _saved_servers.data_changed.connect(_on_store_changed) + if _discovery != null: + if not _discovery.rooms_updated.is_connected( + _on_discovery_rooms_updated + ): + _discovery.rooms_updated.connect(_on_discovery_rooms_updated) + if not _discovery.browse_status_changed.is_connected( + _on_discovery_status_changed + ): + _discovery.browse_status_changed.connect( + _on_discovery_status_changed + ) + if not _discovery.public_join_prepared.is_connected( + _on_public_join_prepared + ): + _discovery.public_join_prepared.connect( + _on_public_join_prepared + ) + if not _discovery.public_join_status_changed.is_connected( + _on_public_join_status_changed + ): + _discovery.public_join_status_changed.connect( + _on_public_join_status_changed + ) _refresh() @@ -140,7 +182,7 @@ func open_page(preserved_endpoint: String = "") -> void: _address.text = "127.0.0.1:7777" show() UtilityPageStyle.animate_in(self) - _set_mode(_mode) + _set_mode(_mode, false) if _mode == Mode.DIRECT: _address.grab_focus() _address.select_all() @@ -148,8 +190,11 @@ func open_page(preserved_endpoint: String = "") -> void: func close_page() -> void: _clear_edit_state() + _discovery_refresh_timer.stop() hide() - get_viewport().gui_release_focus() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() func get_endpoint_text() -> String: @@ -157,15 +202,24 @@ func get_endpoint_text() -> String: func set_status(message: String) -> void: + _connection_error_latched = true _set_status(_friendly_connection_message(message), true) -func _set_mode(mode: Mode) -> void: +func _set_mode(mode: Mode, clear_connection_error: bool = true) -> void: + if clear_connection_error: + _connection_error_latched = false _mode = mode _selected_entry = null + _selected_discovery_index = -1 _clear_edit_state() _refresh_entries() _refresh() + if mode == Mode.DISCOVER: + _discovery_refresh_timer.start() + _request_discovery_refresh() + else: + _discovery_refresh_timer.stop() if not is_visible_in_tree(): return if mode == Mode.DIRECT: @@ -175,16 +229,26 @@ func _set_mode(mode: Mode) -> void: func _request_join() -> void: + _connection_error_latched = false if _network_session.state in [ NetworkSession.State.CONNECTION_FAILED, NetworkSession.State.SERVER_LOST, ]: _network_session.reset_failure() - var endpoint_text: String = ( - _selected_entry.normalized_endpoint - if _mode != Mode.DIRECT and _selected_entry != null - else _address.text - ) + var endpoint_text: String = _address.text + if _mode == Mode.DISCOVER: + var room: Dictionary = _selected_discovery_room() + if _discovery != null and _discovery.is_own_room(room): + _set_status("You are already hosting this room.", true) + return + if _discovery_room_is_full(room): + _set_status("That room is full.", true) + return + if _discovery == null or not _discovery.prepare_public_join(room): + _set_status("Could not prepare the public connection.", true) + return + elif _mode != Mode.DIRECT and _selected_entry != null: + endpoint_text = _selected_entry.normalized_endpoint var endpoint: ConnectionEndpoint = EndpointParser.parse(endpoint_text) if not endpoint.is_valid(): _set_status(endpoint.error_message, true) @@ -194,6 +258,28 @@ func _request_join() -> void: join_requested.emit(endpoint.normalized_display) +func _on_public_join_prepared(endpoint_text: String) -> void: + if _mode != Mode.DISCOVER or not is_visible_in_tree(): + return + var endpoint: ConnectionEndpoint = EndpointParser.parse(endpoint_text) + if not endpoint.is_valid(): + _set_status("Discovery returned an invalid room address.", true) + return + _address.text = endpoint.normalized_display + _set_status("Connecting…") + join_requested.emit(endpoint.normalized_display) + + +func _on_public_join_status_changed(message: String, is_error: bool) -> void: + if _mode == Mode.DISCOVER and is_visible_in_tree(): + if is_error: + _connection_error_latched = true + elif _connection_error_latched: + return + _set_status(message, is_error) + _refresh() + + func _on_save_pressed() -> void: if _name_entry_active: _commit_name_entry() @@ -336,6 +422,13 @@ func _cancel_delete() -> void: func _on_list_item_selected(index: int) -> void: + if _mode == Mode.DISCOVER: + if index < 0 or index >= _discovery_rooms.size(): + return + _selected_discovery_index = index + _selected_entry = null + _refresh() + return if index < 0 or index >= _visible_entries.size(): return _selected_entry = _visible_entries[index] @@ -354,6 +447,23 @@ func _select_entry_id(entry_id: String) -> void: func _refresh_entries() -> void: _visible_entries.clear() _server_list.clear() + if _mode == Mode.DISCOVER: + for room: Dictionary in _discovery_rooms: + var player_count: int = int(room.get("current_players", 0)) + var maximum: int = int(room.get("max_players", 0)) + var own_room: bool = ( + _discovery != null and _discovery.is_own_room(room) + ) + _server_list.add_item( + "%s — %d / %d players%s%s" % [ + str(room.get("room_name", "Public room")), + player_count, + maximum, + " — full" if player_count >= maximum else "", + " — your room" if own_room else "", + ] + ) + return if _saved_servers == null or _mode == Mode.DIRECT: return _visible_entries = ( @@ -389,9 +499,18 @@ func _refresh() -> void: var connecting: bool = _network_session.state in [ NetworkSession.State.CONNECTING, NetworkSession.State.AUTHENTICATING, - ] + ] or (_discovery != null and _discovery.is_public_join_preparing()) var direct: bool = _mode == Mode.DIRECT - var selected: bool = _selected_entry != null + var discovery_mode: bool = _mode == Mode.DISCOVER + var selected: bool = ( + _selected_discovery_index >= 0 + if discovery_mode + else _selected_entry != null + ) + _discover_button.button_pressed = discovery_mode + _direct_button.button_pressed = direct + _saved_button.button_pressed = _mode == Mode.SAVED + _recent_button.button_pressed = _mode == Mode.RECENT _address.visible = direct or _name_entry_active _address_label.visible = _address.visible _address_helper.visible = _address.visible @@ -404,9 +523,26 @@ func _refresh() -> void: _name_helper.visible = _name_entry_active _server_list.visible = not direct and not _name_entry_active _details.visible = not direct and not _name_entry_active - _join_button.disabled = connecting or (not direct and not selected) + _join_button.disabled = ( + connecting + or (not direct and not selected) + or ( + discovery_mode + and selected + and _discovery_room_is_full(_selected_discovery_room()) + ) + or ( + discovery_mode + and selected + and _discovery != null + and _discovery.is_own_room(_selected_discovery_room()) + ) + ) _join_button.text = "join\nnow" if direct else "join" - _save_button.visible = direct or _mode == Mode.RECENT or _name_entry_active + _refresh_button.visible = discovery_mode and not _name_entry_active + _save_button.visible = ( + direct or _mode == Mode.RECENT or _name_entry_active + ) _save_button.disabled = connecting or ( _mode == Mode.RECENT and not selected and not _name_entry_active ) @@ -415,36 +551,50 @@ func _refresh() -> void: _favorite_button.visible = _mode == Mode.SAVED and selected _favorite_button.text = ( "unfavorite" - if selected and _selected_entry.favorite + if _mode == Mode.SAVED + and _selected_entry != null + and _selected_entry.favorite else "favorite" ) - _delete_button.visible = not direct and selected + _delete_button.visible = ( + _mode in [Mode.SAVED, Mode.RECENT] and selected + ) _delete_button.text = "remove" if _mode == Mode.RECENT else "delete" _cancel_button.visible = connecting - _open_close_button.visible = ( - _gameplay_context and _network_session.is_host() - ) - if _open_close_button.visible: - _open_close_button.text = ( - "close\ngame" - if _network_session.is_open_host() - else "open\ngame" - ) _session_summary.visible = ( _gameplay_context and _network_session.state != NetworkSession.State.INACTIVE ) if _session_summary.visible: - _session_summary.text = "%d / %d players" % [ - _network_session.get_player_count(), - _network_session.get_session_max_players(), - ] + _session_summary.text = ( + "UDP %d • %d / %d players" + % [ + _network_session.get_host_port(), + _network_session.get_player_count(), + _network_session.get_session_max_players(), + ] + if _network_session.is_host() + else "%d / %d players" % [ + _network_session.get_player_count(), + _network_session.get_session_max_players(), + ] + ) if not direct: if selected: - _details.text = _format_entry_details(_selected_entry) - elif _visible_entries.is_empty(): _details.text = ( - "No saved servers yet." + _format_discovery_details(_selected_discovery_room()) + if discovery_mode + else _format_entry_details(_selected_entry) + ) + elif ( + _discovery_rooms.is_empty() + if discovery_mode + else _visible_entries.is_empty() + ): + _details.text = ( + "No public rooms are available." + if discovery_mode + else "No saved servers yet." if _mode == Mode.SAVED else "No recent connections yet." ) @@ -452,7 +602,7 @@ func _refresh() -> void: _details.text = "Select a server." var warning: String = ( _saved_servers.get_recovery_warning() - if _saved_servers != null + if _saved_servers != null and not discovery_mode else "" ) if not warning.is_empty() and _status.text.is_empty(): @@ -505,6 +655,76 @@ func _format_entry_details(entry: SavedServerEntry) -> String: return "\n".join(parts) +func _format_discovery_details(room: Dictionary) -> String: + if room.is_empty(): + return "Select a public room." + var lines: Array[String] = [ + "Room: %s" % str(room.get("room_name", "Public room")), + "Players: %d / %d" % [ + int(room.get("current_players", 0)), + int(room.get("max_players", 0)), + ], + "Connection: %s" % ( + _discovery.room_endpoint(room) if _discovery != null else "—" + ), + "Direct UDP connection • ping is measured after joining", + ] + if _discovery != null and _discovery.is_own_room(room): + lines.append("This is the room you are currently hosting.") + return "\n".join(lines) + + +func _selected_discovery_room() -> Dictionary: + if ( + _selected_discovery_index < 0 + or _selected_discovery_index >= _discovery_rooms.size() + ): + return {} + return _discovery_rooms[_selected_discovery_index] + + +func _discovery_room_is_full(room: Dictionary) -> bool: + if room.is_empty(): + return false + return int(room.get("current_players", 0)) >= int(room.get("max_players", 0)) + + +func _request_discovery_refresh() -> void: + if ( + _discovery == null + or _mode != Mode.DISCOVER + or not is_visible_in_tree() + ): + return + _discovery.request_rooms() + + +func _on_discovery_rooms_updated(rooms: Array[Dictionary]) -> void: + var selected_id: String = str( + _selected_discovery_room().get("room_id", "") + ) + _discovery_rooms = rooms.duplicate(true) + _selected_discovery_index = -1 + if _mode == Mode.DISCOVER: + _refresh_entries() + if not selected_id.is_empty(): + for index: int in _discovery_rooms.size(): + if str(_discovery_rooms[index].get("room_id", "")) == selected_id: + _selected_discovery_index = index + _server_list.select(index) + break + _refresh() + + +func _on_discovery_status_changed(message: String, is_error: bool) -> void: + if ( + _mode == Mode.DISCOVER + and is_visible_in_tree() + and not _connection_error_latched + ): + _set_status(message, is_error) + + func _format_result_code(result_code: String) -> String: match result_code.strip_edges().to_upper(): "SUCCESS": @@ -533,6 +753,7 @@ func _friendly_connection_message(message: String) -> String: return "Connection cancelled." if ( "timeout" in normalized + or "timed out" in normalized or "unavailable" in normalized or "refused" in normalized or "reach" in normalized @@ -562,6 +783,7 @@ func _clear_edit_state() -> void: func _on_cancel_pressed() -> void: + _connection_error_latched = false if _network_session != null: _network_session.cancel_connection() _refresh() @@ -583,23 +805,19 @@ func _on_back_pressed() -> void: back_requested.emit() -func _on_open_close_pressed() -> void: - if _network_session == null or not _network_session.is_host(): - return - _network_session.set_host_open(not _network_session.is_open_host()) - _refresh() - - func _on_state_changed(_state: NetworkSession.State) -> void: _refresh() func _on_status_message_changed(message: String) -> void: + if _connection_error_latched: + return _set_status(_friendly_connection_message(message)) _refresh() func _on_connection_error(message: String) -> void: + _connection_error_latched = true _set_status(_friendly_connection_message(message), true) _refresh() diff --git a/ui/network/join_game_page.tscn b/ui/network/join_game_page.tscn index 0337d81..c4be4fa 100644 --- a/ui/network/join_game_page.tscn +++ b/ui/network/join_game_page.tscn @@ -114,22 +114,32 @@ layout_mode = 2 theme_override_constants/separation = 14 alignment = 1 +[node name="DiscoverButton" type="Button" parent="Paper/Margin/Layout/Modes"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 72) +layout_mode = 2 +toggle_mode = true +text = "discover" + [node name="DirectButton" type="Button" parent="Paper/Margin/Layout/Modes"] unique_name_in_owner = true custom_minimum_size = Vector2(100, 72) layout_mode = 2 +toggle_mode = true text = "direct" [node name="SavedButton" type="Button" parent="Paper/Margin/Layout/Modes"] unique_name_in_owner = true custom_minimum_size = Vector2(100, 72) layout_mode = 2 +toggle_mode = true text = "saved" [node name="RecentButton" type="Button" parent="Paper/Margin/Layout/Modes"] unique_name_in_owner = true custom_minimum_size = Vector2(100, 72) layout_mode = 2 +toggle_mode = true text = "recent" [node name="AddressLabel" type="Label" parent="Paper/Margin/Layout"] @@ -163,7 +173,7 @@ unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) theme_override_font_sizes/font_size = 13 -text = "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted.\nJoin Now connects once; Save Server stores this address locally." +text = "Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted; include the port shown by a host when it differs.\nJoin Now connects once; Save Server stores this address locally." horizontal_alignment = 1 autowrap_mode = 2 @@ -258,6 +268,13 @@ layout_mode = 2 theme_override_constants/separation = 7 alignment = 1 +[node name="RefreshButton" type="Button" parent="Paper/Margin/Layout/Actions"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(82, 72) +layout_mode = 2 +text = "refresh" + [node name="JoinButton" type="Button" parent="Paper/Margin/Layout/Actions"] unique_name_in_owner = true custom_minimum_size = Vector2(82, 72) @@ -291,13 +308,6 @@ custom_minimum_size = Vector2(82, 68) layout_mode = 2 text = "delete" -[node name="OpenCloseButton" type="Button" parent="Paper/Margin/Layout/Actions"] -unique_name_in_owner = true -visible = false -custom_minimum_size = Vector2(88, 72) -layout_mode = 2 -text = "open\ngame" - [node name="CancelButton" type="Button" parent="Paper/Margin/Layout/Actions"] unique_name_in_owner = true visible = false diff --git a/ui/on_screen_keyboard.gd b/ui/on_screen_keyboard.gd new file mode 100644 index 0000000..0d7315d --- /dev/null +++ b/ui/on_screen_keyboard.gd @@ -0,0 +1,456 @@ +class_name OnScreenKeyboard +extends Control + +const ControllerFocusNavigationType = preload( + "res://ui/controller_focus_navigation.gd" +) +const CHECK_ICON: Texture2D = preload( + "res://ui/icons/pictograms/check_mark_dark.png" +) +const CHARACTER_KEY_SIZE: Vector2 = Vector2(96.0, 72.0) +const CHARACTER_FONT_SIZE: int = 34 +const TRIGGER_PRESS_THRESHOLD: float = 0.55 +const TRIGGER_RELEASE_THRESHOLD: float = 0.25 + +signal text_submitted(value: String) + +enum Page { + LOWER, + UPPER, + SYMBOLS, +} + +var _enabled: bool = false +var _page: Page = Page.LOWER +var _target: Control +var _target_virtual_keyboard_enabled: bool = true +var _buffer: String = "" +var _preview: Label +var _page_buttons: Array[Button] = [] +var _keys_host: VBoxContainer +var _key_buttons: Array[Button] = [] +var _caret_left_button: Button +var _caret_right_button: Button +var _backspace_button: Button +var _space_button: Button +var _check_button: Button +var _left_trigger_pressed: bool = false +var _right_trigger_pressed: bool = false + + +func _ready() -> void: + process_mode = Node.PROCESS_MODE_ALWAYS + set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + z_index = 1000 + mouse_filter = Control.MOUSE_FILTER_STOP + _build_interface() + hide() + set_process_input(true) + + +func set_enabled(enabled: bool) -> void: + _enabled = enabled + if not _enabled and visible: + _close_keyboard(false) + + +func is_enabled() -> bool: + return _enabled + + +func is_open() -> bool: + return visible + + +func _input(event: InputEvent) -> void: + if not _enabled: + return + if visible: + var joy_motion := event as InputEventJoypadMotion + if joy_motion != null and _handle_trigger_shortcut(joy_motion): + get_viewport().set_input_as_handled() + return + var joy_button := event as InputEventJoypadButton + if joy_button != null and joy_button.pressed: + if joy_button.button_index == JOY_BUTTON_X: + _backspace() + get_viewport().set_input_as_handled() + return + if joy_button.button_index == JOY_BUTTON_LEFT_SHOULDER: + _close_keyboard(false) + get_viewport().set_input_as_handled() + return + if joy_button.button_index == JOY_BUTTON_RIGHT_SHOULDER: + _set_page(wrapi(int(_page) + 1, 0, Page.size())) + get_viewport().set_input_as_handled() + return + if event.is_action_pressed(&"ui_cancel"): + _close_keyboard(true) + get_viewport().set_input_as_handled() + return + var joy_event := event as InputEventJoypadButton + if ( + joy_event == null + or not joy_event.pressed + or ( + joy_event.button_index != JOY_BUTTON_A + and not event.is_action_pressed(&"ui_accept") + ) + ): + return + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if _can_edit(focus_owner): + _open_for(focus_owner) + get_viewport().set_input_as_handled() + + +func _can_edit(control: Control) -> bool: + if control is LineEdit: + return (control as LineEdit).editable + if control is TextEdit: + return (control as TextEdit).editable + return false + + +func _open_for(control: Control) -> void: + _target = control + _target_virtual_keyboard_enabled = bool( + _target.get("virtual_keyboard_enabled") + ) + _target.set("virtual_keyboard_enabled", false) + _buffer = str(_target.get("text")) + _page = Page.LOWER + show() + _refresh_preview() + _rebuild_keys() + + +func _close_keyboard(restore_focus: bool) -> void: + var prior_target: Control = _target + if is_instance_valid(prior_target): + prior_target.set( + "virtual_keyboard_enabled", + _target_virtual_keyboard_enabled + ) + hide() + get_viewport().gui_release_focus() + _target = null + if restore_focus and is_instance_valid(prior_target): + prior_target.grab_focus() + + +func _submit() -> void: + var submitted_target: Control = _target + var submitted_text: String = _buffer + _close_keyboard(false) + if submitted_target is LineEdit: + (submitted_target as LineEdit).text_submitted.emit(submitted_text) + elif submitted_target is TextEdit: + (submitted_target as TextEdit).text_changed.emit() + text_submitted.emit(submitted_text) + + +func _type_character(character: String) -> void: + if not is_instance_valid(_target): + _close_keyboard(false) + return + var caret: int = _get_caret_column() + var next_text: String = ( + _buffer.substr(0, caret) + + character + + _buffer.substr(caret) + ) + if _target is LineEdit: + var line_edit := _target as LineEdit + if line_edit.max_length > 0 and next_text.length() > line_edit.max_length: + return + _buffer = next_text + _set_target_text(caret + character.length()) + + +func _type_space() -> void: + _type_character(" ") + + +func _backspace() -> void: + if not is_instance_valid(_target) or _buffer.is_empty(): + return + var caret: int = _get_caret_column() + if caret <= 0: + return + _buffer = _buffer.erase(caret - 1, 1) + _set_target_text(caret - 1) + + +func _move_caret(direction: int) -> void: + if not is_instance_valid(_target): + _close_keyboard(false) + return + var caret: int = clampi( + _get_caret_column() + direction, + 0, + _buffer.length(), + ) + if _target is LineEdit: + (_target as LineEdit).caret_column = caret + elif _target is TextEdit: + (_target as TextEdit).set_caret_column(caret) + _refresh_preview() + + +func _handle_trigger_shortcut(event: InputEventJoypadMotion) -> bool: + if event.axis == JOY_AXIS_TRIGGER_LEFT: + if event.axis_value <= TRIGGER_RELEASE_THRESHOLD: + _left_trigger_pressed = false + elif ( + event.axis_value >= TRIGGER_PRESS_THRESHOLD + and not _left_trigger_pressed + ): + _left_trigger_pressed = true + _move_caret(-1) + return true + elif event.axis == JOY_AXIS_TRIGGER_RIGHT: + if event.axis_value <= TRIGGER_RELEASE_THRESHOLD: + _right_trigger_pressed = false + elif ( + event.axis_value >= TRIGGER_PRESS_THRESHOLD + and not _right_trigger_pressed + ): + _right_trigger_pressed = true + _move_caret(1) + return true + return false + + +func _get_caret_column() -> int: + if _target is LineEdit: + return clampi( + (_target as LineEdit).caret_column, + 0, + _buffer.length() + ) + return _buffer.length() + + +func _set_target_text(caret: int) -> void: + if _target is LineEdit: + var line_edit := _target as LineEdit + line_edit.text = _buffer + line_edit.caret_column = caret + line_edit.text_changed.emit(_buffer) + elif _target is TextEdit: + var text_edit := _target as TextEdit + text_edit.text = _buffer + text_edit.text_changed.emit() + _refresh_preview() + + +func _refresh_preview() -> void: + if _preview == null: + return + var displayed_text: String = _buffer + if _target is LineEdit and (_target as LineEdit).secret: + displayed_text = "*".repeat(_buffer.length()) + var caret: int = clampi(_get_caret_column(), 0, displayed_text.length()) + _preview.text = displayed_text.insert(caret, "|") + + +func _set_page(page_index: int) -> void: + _page = page_index as Page + _rebuild_keys() + + +func _rebuild_keys() -> void: + for child: Node in _keys_host.get_children(): + _keys_host.remove_child(child) + child.queue_free() + _key_buttons.clear() + var rows: Array = _rows_for_page() + for row_value: Variant in rows: + var row := HBoxContainer.new() + row.alignment = BoxContainer.ALIGNMENT_CENTER + row.size_flags_vertical = Control.SIZE_SHRINK_CENTER + row.add_theme_constant_override("separation", 10) + _keys_host.add_child(row) + for character_value: Variant in row_value as Array: + var character: String = str(character_value) + var key_button: Button = _make_button(character, CHARACTER_KEY_SIZE) + key_button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + key_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER + key_button.add_theme_font_size_override( + "font_size", CHARACTER_FONT_SIZE + ) + key_button.pressed.connect(_type_character.bind(character)) + row.add_child(key_button) + _key_buttons.append(key_button) + for index: int in _page_buttons.size(): + _page_buttons[index].button_pressed = index == int(_page) + call_deferred("_configure_key_focus") + + +func _configure_key_focus() -> void: + var focus_controls: Array[Control] = [] + for button: Button in _key_buttons: + focus_controls.append(button) + for button: Button in [ + _caret_left_button, + _caret_right_button, + _backspace_button, + _space_button, + _check_button, + ]: + focus_controls.append(button) + ControllerFocusNavigationType.configure_spatial_neighbors(focus_controls) + if not _key_buttons.is_empty(): + _key_buttons[0].grab_focus() + + +func _rows_for_page() -> Array: + match _page: + Page.UPPER: + return [ + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M"], + ] + Page.SYMBOLS: + return [ + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], + ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], + ["-", "_", "=", "+", "[", "]", "{", "}"], + [".", ",", "?", "/", ":", ";", "'", "\""] + ] + _: + return [ + ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], + ["a", "s", "d", "f", "g", "h", "j", "k", "l"], + ["z", "x", "c", "v", "b", "n", "m"], + ] + + +func _build_interface() -> void: + var backdrop := ColorRect.new() + backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + backdrop.color = Color(0.012, 0.075, 0.105, 0.82) + backdrop.mouse_filter = Control.MOUSE_FILTER_STOP + add_child(backdrop) + var screen_margin := MarginContainer.new() + screen_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + for side: StringName in [ + &"margin_left", &"margin_top", &"margin_right", &"margin_bottom" + ]: + screen_margin.add_theme_constant_override(side, 14) + add_child(screen_margin) + var panel := PanelContainer.new() + panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL + panel.size_flags_vertical = Control.SIZE_EXPAND_FILL + panel.add_theme_stylebox_override( + "panel", + _make_style(Color(0.075, 0.27, 0.34, 0.98), 18, 4) + ) + screen_margin.add_child(panel) + var margin := MarginContainer.new() + for side: StringName in [&"margin_left", &"margin_top", &"margin_right", &"margin_bottom"]: + margin.add_theme_constant_override(side, 18) + panel.add_child(margin) + var layout := VBoxContainer.new() + layout.add_theme_constant_override("separation", 10) + margin.add_child(layout) + var preview_panel := PanelContainer.new() + preview_panel.custom_minimum_size = Vector2(0.0, 86.0) + preview_panel.add_theme_stylebox_override( + "panel", + _make_style(Color(0.82, 0.94, 0.95, 1.0), 10, 2) + ) + layout.add_child(preview_panel) + _preview = Label.new() + _preview.add_theme_color_override("font_color", Color(0.025, 0.12, 0.17, 1.0)) + _preview.add_theme_font_size_override("font_size", 30) + _preview.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + _preview.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _preview.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + _preview.add_theme_constant_override("outline_size", 0) + preview_panel.add_child(_preview) + var page_row := HBoxContainer.new() + page_row.alignment = BoxContainer.ALIGNMENT_CENTER + page_row.add_theme_constant_override("separation", 12) + layout.add_child(page_row) + for page_name: String in ["lower", "upper", "symbols"]: + var page_button: Button = _make_button(page_name, Vector2(150.0, 44.0)) + page_button.toggle_mode = true + page_button.focus_mode = Control.FOCUS_NONE + page_button.pressed.connect(_set_page.bind(_page_buttons.size())) + page_row.add_child(page_button) + _page_buttons.append(page_button) + _keys_host = VBoxContainer.new() + _keys_host.size_flags_vertical = Control.SIZE_EXPAND_FILL + _keys_host.alignment = BoxContainer.ALIGNMENT_CENTER + _keys_host.add_theme_constant_override("separation", 12) + layout.add_child(_keys_host) + var utility_row := HBoxContainer.new() + utility_row.alignment = BoxContainer.ALIGNMENT_CENTER + utility_row.add_theme_constant_override("separation", 12) + layout.add_child(utility_row) + _caret_left_button = _make_button("LT <", Vector2(118.0, 64.0)) + _caret_left_button.tooltip_text = "move text cursor left" + _caret_left_button.pressed.connect(_move_caret.bind(-1)) + utility_row.add_child(_caret_left_button) + _caret_right_button = _make_button("> RT", Vector2(118.0, 64.0)) + _caret_right_button.tooltip_text = "move text cursor right" + _caret_right_button.pressed.connect(_move_caret.bind(1)) + utility_row.add_child(_caret_right_button) + _backspace_button = _make_button("backspace X", Vector2(180.0, 64.0)) + _backspace_button.pressed.connect(_backspace) + utility_row.add_child(_backspace_button) + _space_button = _make_button("space", Vector2(350.0, 64.0)) + _space_button.size_flags_stretch_ratio = 4.0 + _space_button.pressed.connect(_type_space) + utility_row.add_child(_space_button) + _check_button = _make_button("", Vector2(92.0, 64.0)) + _check_button.icon = CHECK_ICON + _check_button.expand_icon = true + _check_button.alignment = HORIZONTAL_ALIGNMENT_CENTER + _check_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + _check_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + _check_button.tooltip_text = "submit" + _check_button.accessibility_name = "submit text" + _check_button.add_theme_constant_override("icon_max_width", 42) + _check_button.pressed.connect(_submit) + utility_row.add_child(_check_button) + _rebuild_keys() + + +func _make_button(label: String, minimum_size: Vector2) -> Button: + var button := Button.new() + button.text = label + button.custom_minimum_size = minimum_size + button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + button.size_flags_vertical = Control.SIZE_EXPAND_FILL + button.focus_mode = Control.FOCUS_ALL + button.add_theme_font_size_override("font_size", 22) + button.add_theme_color_override("font_color", Color(0.025, 0.12, 0.17, 1.0)) + button.add_theme_color_override("font_hover_color", Color(0.025, 0.12, 0.17, 1.0)) + button.add_theme_color_override("font_focus_color", Color(0.025, 0.12, 0.17, 1.0)) + button.add_theme_stylebox_override( + "normal", + _make_style(Color(0.72, 0.88, 0.91, 1.0), 10, 2) + ) + button.add_theme_stylebox_override( + "hover", + _make_style(Color(0.87, 0.96, 0.96, 1.0), 10, 3) + ) + button.add_theme_stylebox_override("focus", button.get_theme_stylebox("hover")) + button.add_theme_stylebox_override( + "pressed", + _make_style(Color(0.44, 0.72, 0.77, 1.0), 10, 3) + ) + return button + + +func _make_style(color: Color, radius: int, border_width: int) -> StyleBoxFlat: + var style := StyleBoxFlat.new() + style.bg_color = color + style.border_color = Color(0.025, 0.12, 0.17, 1.0) + style.set_border_width_all(border_width) + style.set_corner_radius_all(radius) + return style diff --git a/ui/on_screen_keyboard.gd.uid b/ui/on_screen_keyboard.gd.uid new file mode 100644 index 0000000..da300a2 --- /dev/null +++ b/ui/on_screen_keyboard.gd.uid @@ -0,0 +1 @@ +uid://dcanibf8hordh diff --git a/ui/pause_menu.gd b/ui/pause_menu.gd index f13003e..dccac73 100644 --- a/ui/pause_menu.gd +++ b/ui/pause_menu.gd @@ -107,6 +107,7 @@ func setup( network_session: NetworkSessionType, saved_servers: SavedServerStoreType, server_trust: ServerTrustStore, + discovery: DiscoveryClient, ) -> void: _player = player _save_manager = save_manager @@ -114,7 +115,9 @@ func setup( _fishing_spot = fishing_spot _network_session = network_session _saved_servers = saved_servers - _join_game_page.setup(network_session, saved_servers, true, server_trust) + _join_game_page.setup( + network_session, saved_servers, true, server_trust, discovery + ) if not _fishing_spot.bite_activated.is_connected(_on_bite_activated): _fishing_spot.bite_activated.connect(_on_bite_activated) @@ -348,7 +351,10 @@ func _open_confirmation( else BubbleConfirmationPageType.InitialFocus.CONFIRM ) ) - _begin_root_exit(_show_confirmation) + # Confirmation actions reject input while the root page is still leaving. + # Finish that exit before exposing an interactive confirmation page so a + # pointer click can never land in the overlap window and be discarded. + _begin_root_exit(_show_confirmation, false) func _show_confirmation() -> void: @@ -447,7 +453,10 @@ func _finish_root_entry(generation: int) -> void: _root_transition_active = false -func _begin_root_exit(completed: Callable) -> void: +func _begin_root_exit( + completed: Callable, + overlap_next_page: bool = true, +) -> void: if _root_transition_active or not _root_page.visible: return _root_transition_generation += 1 @@ -455,9 +464,14 @@ func _begin_root_exit(completed: Callable) -> void: _emit_transition_flurry() var generation: int = _root_transition_generation _root_page.transition_out( - _finish_root_exit.bind(generation), + _finish_root_exit.bind( + generation, + Callable() if overlap_next_page else completed, + ), 0.0 ) + if not overlap_next_page: + return await get_tree().create_timer( UIMotion.BUBBLE_TRANSITION_OVERLAP_DELAY ).timeout @@ -468,10 +482,13 @@ func _begin_root_exit(completed: Callable) -> void: func _finish_root_exit( generation: int, + completed: Callable, ) -> void: if generation != _root_transition_generation or not visible: return _root_transition_active = false + if completed.is_valid(): + completed.call() func _finish_user_close() -> void: @@ -495,7 +512,9 @@ func _finish_close(reason: CloseReason, restore_controls: bool) -> void: hide() if _fishing_spot != null and is_instance_valid(_fishing_spot): _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) - get_viewport().gui_release_focus() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() if restore_controls: _restore_controls() else: diff --git a/ui/pause_menu.tscn b/ui/pause_menu.tscn index 2477f23..941e26e 100644 --- a/ui/pause_menu.tscn +++ b/ui/pause_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3] +[gd_scene load_steps=16 format=3] [ext_resource type="Script" path="res://ui/pause_menu.gd" id="1_script"] [ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="2_settings"] @@ -10,6 +10,11 @@ [ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_transition_flurry.gd" id="8_flurry"] [ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_confirmation_page.tscn" id="9_confirmation"] [ext_resource type="PackedScene" path="res://ui/network/join_game_page.tscn" id="10_join_page"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="11_online_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/save_dark.png" id="12_save_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/settings_dark.png" id="13_settings_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_dark.png" id="14_undo_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="15_x_dark"] [node name="PauseMenu" type="Control"] unique_name_in_owner = true @@ -87,7 +92,9 @@ compact_reference_size = Vector2(608, 448) [node name="ResumeButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) -text = "return\nto game" +texture_filter = 1 +icon = ExtResource("14_undo_dark") +tooltip_text = "return to game" accessibility_name = "return to game" neutral_size = Vector2(174, 164) desktop_anchor = Vector2(335, 220) @@ -100,7 +107,9 @@ motion_phase = 0.3 [node name="SaveButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) -text = "save\nnow" +texture_filter = 1 +icon = ExtResource("12_save_dark") +tooltip_text = "save now" accessibility_name = "save now" neutral_size = Vector2(126, 120) desktop_anchor = Vector2(185, 185) @@ -113,7 +122,10 @@ motion_phase = 1.15 [node name="SettingsButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) -text = "settings" +texture_filter = 1 +icon = ExtResource("13_settings_dark") +tooltip_text = "settings" +accessibility_name = "settings" neutral_size = Vector2(144, 136) desktop_anchor = Vector2(485, 170) compact_anchor = Vector2(515, 175) @@ -125,7 +137,9 @@ motion_phase = 2.05 [node name="JoinGameButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) -text = "join\ngame" +texture_filter = 1 +icon = ExtResource("11_online_dark") +tooltip_text = "join game" accessibility_name = "join game" neutral_size = Vector2(126, 120) desktop_anchor = Vector2(105, 320) @@ -164,7 +178,10 @@ motion_phase = 3.9 [node name="QuitButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) -text = "quit" +texture_filter = 1 +icon = ExtResource("15_x_dark") +tooltip_text = "quit" +accessibility_name = "quit" neutral_size = Vector2(102, 98) desktop_anchor = Vector2(390, 395) compact_anchor = Vector2(390, 410) diff --git a/ui/player_menu.gd b/ui/player_menu.gd index dd13792..7f73acd 100644 --- a/ui/player_menu.gd +++ b/ui/player_menu.gd @@ -14,6 +14,9 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd") const InventoryNotepadType = preload( "res://ui/components/inventory_notepad.gd" ) +const CurrencyPresentationType = preload( + "res://ui/currency_presentation.gd" +) const OrganizerTabType = preload("res://ui/components/organizer_tab.gd") const FishPoolType = preload("res://fish/fish_pool.gd") const FishingSpotType = preload("res://fishing/fishing_spot.gd") @@ -27,6 +30,12 @@ const PlayerHotbarType = preload("res://inventory/player_hotbar.gd") const PlayerCoolerCapacityType = preload( "res://progression/player_cooler_capacity.gd" ) +const PlayerExperienceType = preload( + "res://progression/player_experience.gd" +) +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) const FishBatchSelectionType = preload( "res://ui/fish_batch_selection.gd" ) @@ -79,6 +88,14 @@ const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop" signal menu_visibility_changed(is_open: bool) signal inventory_hotbar_context_changed(show_hotbar: bool) +signal controller_hotbar_placement_requested( + assignment_kind: PlayerHotbarType.AssignmentKind, + identity: StringName, + initial_slot: int, +) +signal controller_hotbar_placement_ended +signal controller_hotbar_management_requested(initial_slot: int) +signal controller_hotbar_management_ended signal menu_exit_started signal shop_cooler_modal_changed(is_open: bool) @@ -87,6 +104,7 @@ enum Section { BAG, TACKLE_BOX, LOGBOOK, + NET, MAIL, PROFILE, PLAYERS, @@ -117,6 +135,13 @@ enum CloseReason { TEARDOWN, } +enum ControllerOwnership { + ITEM_LIST, + NOTEPAD_ACTIONS, + HOTBAR_MANAGEMENT, + HOTBAR_PLACEMENT, +} + const INVENTORY_MAIN_POSITION := Vector2(54.0, 166.0) const INVENTORY_MAIN_SIZE := Vector2(882.0, 484.0) const INVENTORY_PANEL_GAP := 16.0 @@ -138,7 +163,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _cooler_inner_liner: PanelContainer = %CoolerInnerLiner @onready var _cooler_water_surface: ColorRect = %WaterSurface @onready var _fish_field: Control = %FishField -@onready var _cooler_empty: Label = %CoolerEmpty @onready var _cooler_sort_controls: HBoxContainer = %CoolerSortControls @onready var _cooler_sort_option: NotepadInkChoiceType = %CoolerSortOption @onready var _cooler_sort_direction: NotepadInkActionType = %CoolerSortDirection @@ -147,7 +171,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _notepad_binding: Label = %BindingDecoration @onready var _notepad_title: Label = %NotepadTitle @onready var _notepad_rule: ColorRect = %NotepadRule -@onready var _notepad_wallet_value: Label = %NotepadWalletValue +@onready var _notepad_wallet_value: CurrencyAmount = %NotepadWalletValue @onready var _notepad_capacity_value: Label = %NotepadCapacityValue @onready var _cooler_detail_texture: TextureRect = %CoolerDetailTexture @onready var _cooler_detail_name: Label = %CoolerDetailName @@ -157,7 +181,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _cooler_weight_unit: Label = %CoolerWeightUnit @onready var _cooler_offer_row: HBoxContainer = %CoolerOfferRow @onready var _cooler_offer_label: Label = %CoolerOfferLabel -@onready var _cooler_offer_value: Label = %CoolerOfferValue +@onready var _cooler_offer_value: CurrencyAmount = %CoolerOfferValue @onready var _cooler_selection_summary: Control = %CoolerSelectionSummary @onready var _cooler_selection_empty: Label = %CoolerSelectionEmpty @onready var _cooler_selected_count_row: HBoxContainer = %CoolerSelectedCountRow @@ -165,9 +189,10 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _cooler_selected_count_label: Label = %CoolerSelectedCountLabel @onready var _cooler_combined_offer_row: HBoxContainer = %CoolerCombinedOfferRow @onready var _cooler_combined_offer_label: Label = %CoolerCombinedOfferLabel -@onready var _cooler_combined_offer_value: Label = %CoolerCombinedOfferValue +@onready var _cooler_combined_offer_value: CurrencyAmount = %CoolerCombinedOfferValue @onready var _favorite_bubble: NotepadInkActionType = %FavoriteBubble @onready var _sell_bubble: NotepadInkActionType = %SellBubble +@onready var _sell_all_bubble: NotepadInkActionType = %SellAllBubble @onready var _bag_page: Control = %BagPage @onready var _tackle_box_page: Control = %TackleBoxPage @onready var _inventory_sub_tabs: HBoxContainer = %InventorySubTabs @@ -183,6 +208,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _lures_filter: Button = %LuresFilter @onready var _tackle_empty: Label = %TackleEmpty @onready var _tackle_detail_text: Label = %TackleDetailText +@onready var _tackle_equip_button: NotepadInkActionType = %TackleEquipButton @onready var _tackle_item_list: VBoxContainer = %TackleItemList @onready var _bag_outer_wall: PanelContainer = %BagOuterWall @onready var _bag_inner_liner: PanelContainer = %BagInnerLiner @@ -191,12 +217,12 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _bag_item_field: Control = %BagItemField @onready var _bag_empty_state: Label = %BagEmptyState @onready var _bag_detail_constellation: Control = %BagDetailConstellation -@onready var _bag_sprite_detail_bubble: Control = %BagDetailBubble @onready var _bag_sprite_detail_texture: TextureRect = %BagSpriteDetailTexture @onready var _bag_sprite_detail_name: Label = %BagSpriteDetailName @onready var _bag_sprite_detail_data: Label = %BagSpriteDetailData @onready var _logbook_page: Control = %LogbookPage @onready var _catalog_logbook: LogbookPage = %CatalogLogbook +@onready var _the_net_page: TheNetPage = %TheNetPage @onready var _mail_page: MailPage = %MailPage @onready var _profile_page: ProfilePage = %ProfilePage @onready var _players_page: PlayersPage = %PlayersPage @@ -204,7 +230,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _book_spread: BoxContainer = %BookSpread @onready var _left_page: PanelContainer = %LeftPage @onready var _right_page: PanelContainer = %RightPage -@onready var _book_gutter: ColorRect = %BookGutter @onready var _left_heading: Label = %LeftHeading @onready var _right_heading: Label = %RightHeading @onready var _left_entry_field: BoxContainer = %LeftEntryField @@ -215,6 +240,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _logbook_page_status: BubbleStatusBubbleType = %LogbookPageStatus @onready var _inventory_tab: BubbleButtonType = %InventoryTab @onready var _logbook_tab: BubbleButtonType = %LogbookTab +@onready var _the_net_tab: BubbleButtonType = %TheNetTab @onready var _mail_tab: BubbleButtonType = %MailTab @onready var _profile_tab: BubbleButtonType = %ProfileTab @onready var _players_tab: BubbleButtonType = %PlayersTab @@ -224,7 +250,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _content_stage: Control = %Content @onready var _cooler_scroll: ScrollContainer = %CoolerScroll @onready var _cooler_host: Control = %CoolerHost -@onready var _wallet_balance: Label = %WalletBalance +@onready var _wallet_balance: CurrencyAmount = %WalletBalance @onready var _header: Control = %Header @onready var _separator: Control = %Separator @onready var _wallet_status: BubbleStatusBubbleType = %WalletStatus @@ -234,7 +260,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _offer_status: BubbleStatusBubbleType = %OfferStatus @onready var _inventory_section: Control = %InventorySection @onready var _bag_section: Control = %BagSection -@onready var _logbook_section: Control = %LogbookSection @onready var _bag_empty: Label = %BagEmpty @onready var _bag_grid: GridContainer = %BagGrid @onready var _bag_list: Control = %BagList @@ -244,23 +269,19 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0) @onready var _bag_detail_data: Label = %BagDetailData @onready var _sort_option: OptionButton = %SortOption @onready var _sort_direction: Button = %SortDirection -@onready var _held_value: Label = %HeldValue +@onready var _held_value: CurrencyAmount = %HeldValue @onready var _cooler_count: Label = %CoolerCount @onready var _inventory_empty: Label = %InventoryEmpty -@onready var _detail_texture: TextureRect = %DetailTexture -@onready var _detail_name: Label = %DetailName -@onready var _detail_data: Label = %DetailData @onready var _selection_summary: Label = %SelectionSummary @onready var _favorite_button: Button = %FavoriteButton @onready var _sell_button: Button = %SellButton @onready var _sale_unavailable: Label = %SaleUnavailable -@onready var _transaction_feedback: Label = %TransactionFeedback +@onready var _transaction_feedback: RichTextLabel = %TransactionFeedback @onready var _sale_confirmation: PanelContainer = %SaleConfirmation -@onready var _confirmation_message: Label = %ConfirmationMessage +@onready var _confirmation_message: RichTextLabel = %ConfirmationMessage @onready var _confirm_sale_button: Button = %ConfirmSaleButton @onready var _cancel_sale_button: Button = %CancelSaleButton @onready var _logbook_empty: Label = %LogbookEmpty -@onready var _logbook_grid: GridContainer = %LogbookGrid var _compact_layout: bool = false var _player: PlayerType @@ -274,6 +295,7 @@ var _network_mail_service: NetworkMailService var _reservations: PlayerAssetReservationService var _network_profile_service: NetworkProfileService var _network_player_list: NetworkPlayerListService +var _controller_mapping_manager: ControllerMappingManagerType var _default_buyer: FishBuyerProfileType var _sale_buyer_override: FishBuyerProfileType var _shop_cooler_context_active: bool = false @@ -292,6 +314,15 @@ var _last_inventory_section: Section = Section.COOLER var _bag_view: BagView = BagView.EQUIPMENT var _tackle_view: TackleView = TackleView.BAIT var _selected_tackle_item_id: StringName +var _controller_ownership: ControllerOwnership = ControllerOwnership.ITEM_LIST +var _controller_source_section: Section = Section.COOLER +var _controller_source_identity: StringName +var _controller_notepad_actions: Array[BaseButton] = [] +var _controller_hotbar_assignment_kind: PlayerHotbarType.AssignmentKind = ( + PlayerHotbarType.AssignmentKind.EMPTY +) +var _controller_hotbar_identity: StringName +var _controller_previous_hotbar_slot: int = 0 var _sort_mode: SortMode = SortMode.CATCH_ORDER var _sort_descending: bool = true var _fish_selection := FishBatchSelectionType.new() @@ -319,6 +350,7 @@ var _cooler_rest_position: Vector2 = Vector2.ZERO var _bag_rest_position: Vector2 = Vector2.ZERO var _tackle_rest_position: Vector2 = Vector2.ZERO var _logbook_rest_position: Vector2 = Vector2.ZERO +var _the_net_rest_position: Vector2 = Vector2.ZERO var _mail_rest_position: Vector2 = Vector2.ZERO var _profile_rest_position: Vector2 = Vector2.ZERO var _page_outgoing_root: Control @@ -357,9 +389,11 @@ func _ready() -> void: ) _bait_filter.pressed.connect(_set_tackle_view.bind(TackleView.BAIT)) _lures_filter.pressed.connect(_set_tackle_view.bind(TackleView.LURES)) + _tackle_equip_button.pressed.connect(_toggle_active_tackle) _logbook_tab.pressed.connect( _show_section.bind(Section.LOGBOOK) ) + _the_net_tab.pressed.connect(_show_section.bind(Section.NET)) _mail_tab.pressed.connect(_show_section.bind(Section.MAIL)) _profile_tab.pressed.connect(_show_section.bind(Section.PROFILE)) _players_tab.pressed.connect(_show_section.bind(Section.PLAYERS)) @@ -372,6 +406,7 @@ func _ready() -> void: _sell_button.pressed.connect(_on_sell_pressed) _favorite_bubble.pressed.connect(_on_favorite_pressed) _sell_bubble.pressed.connect(_on_sell_pressed) + _sell_all_bubble.pressed.connect(_on_sell_all_pressed) _confirm_sale_button.pressed.connect(_on_confirm_sale_pressed) _cancel_sale_button.pressed.connect(_close_sale_confirmation) _configure_sale_confirmation_focus() @@ -387,6 +422,7 @@ func _ready() -> void: _navigation_cluster.configure([ _inventory_tab, _logbook_tab, + _the_net_tab, _mail_tab, _profile_tab, _players_tab, @@ -477,6 +513,7 @@ func setup( player: PlayerType, inventory: FishInventoryType, collection_log: CollectionLogType, + experience: PlayerExperienceType, wallet: PlayerWalletType, sale_service: FishSaleServiceType, default_buyer: FishBuyerProfileType, @@ -492,6 +529,11 @@ func setup( reservations: PlayerAssetReservationService, network_profile_service: NetworkProfileService, network_player_list: NetworkPlayerListService, + discovery: DiscoveryClient, + player_jobs: PlayerJobService, + world_time: WorldTimeService, + world_environment: WorldEnvironment, + world_sun: DirectionalLight3D, ) -> void: _player = player _inventory = inventory @@ -514,9 +556,15 @@ func setup( _mail_page.setup( network_mail_service, reservations, inventory, wallet, bag, item_catalog ) - _profile_page.setup(network_profile_service) - _players_page.setup(network_player_list) + _profile_page.setup( + network_profile_service, + experience, + world_environment, + world_sun, + ) + _players_page.setup(network_player_list, discovery) _catalog_logbook.setup(collection_log, inventory, catalog) + _the_net_page.setup(player_jobs, world_time) _network_mail_service.unread_count_changed.connect( _on_mail_unread_count_changed ) @@ -542,6 +590,10 @@ func setup( _fishing_spot.bite_activated.connect(_on_bite_activated) if not _bag.contents_changed.is_connected(_on_bag_changed): _bag.contents_changed.connect(_on_bag_changed) + if not _player.active_bait_changed.is_connected(_on_active_bait_changed): + _player.active_bait_changed.connect(_on_active_bait_changed) + if not _player.active_lure_changed.is_connected(_on_active_lure_changed): + _player.active_lure_changed.connect(_on_active_lure_changed) if not _hotbar.slots_changed.is_connected(_on_hotbar_changed): _hotbar.slots_changed.connect(_on_hotbar_changed) if not _cooler_capacity.capacity_changed.is_connected( @@ -551,9 +603,36 @@ func setup( _refresh_all() +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + _profile_page.setup_controller_mapping(_controller_mapping_manager) + + +func set_profile_preview_world_pixel_size(pixel_size: int) -> void: + _profile_page.set_world_pixel_size(pixel_size) + + +var _left_page_trigger_held: bool = false +var _right_page_trigger_held: bool = false + + func _input(event: InputEvent) -> void: if event is InputEventKey and event.echo: return + if _handle_controller_ownership_input(event): + get_viewport().set_input_as_handled() + return + if visible: + _reserve_main_navigation_for_page_switching() + _reserve_visible_secondary_navigation() + if _handle_controller_page_switch(event): + get_viewport().set_input_as_handled() + return + if _handle_controller_secondary_switch(event): + get_viewport().set_input_as_handled() + return if _handle_direct_page_shortcut(event): get_viewport().set_input_as_handled() return @@ -572,6 +651,560 @@ func _input(event: InputEvent) -> void: get_viewport().set_input_as_handled() +func _handle_controller_ownership_input(event: InputEvent) -> bool: + if not visible: + return false + var button_event := event as InputEventJoypadButton + var accept_pressed: bool = ( + button_event != null + and button_event.pressed + and _event_matches_controller_role( + event, + ControllerMappingManagerType.ROLE_A, + JOY_BUTTON_A, + ) + ) + var cancel_pressed: bool = ( + button_event != null + and button_event.pressed + and _event_matches_controller_role( + event, + ControllerMappingManagerType.ROLE_B, + JOY_BUTTON_B, + ) + ) + var alternate_pressed: bool = ( + button_event != null + and button_event.pressed + and _event_matches_controller_role( + event, + ControllerMappingManagerType.ROLE_Y, + JOY_BUTTON_Y, + ) + ) + if _controller_ownership == ControllerOwnership.HOTBAR_MANAGEMENT: + if cancel_pressed: + _release_controller_ownership(true, false) + return true + if alternate_pressed: + if _hotbar != null: + _hotbar.clear_slot(_hotbar.get_selected_slot()) + return true + if accept_pressed: + return true + return false + if _controller_ownership == ControllerOwnership.HOTBAR_PLACEMENT: + if accept_pressed: + _confirm_controller_hotbar_placement() + return true + if cancel_pressed: + _release_controller_ownership(true, true) + return true + if alternate_pressed: + return true + return false + if _controller_ownership == ControllerOwnership.NOTEPAD_ACTIONS: + if cancel_pressed: + _release_controller_ownership(true, false) + return true + if alternate_pressed: + return true + var direction: int = 0 + if event.is_action_pressed("ui_left") or event.is_action_pressed("ui_up"): + direction = -1 + elif ( + event.is_action_pressed("ui_right") + or event.is_action_pressed("ui_down") + ): + direction = 1 + if direction != 0: + _focus_next_notepad_action(direction) + return true + return false + if alternate_pressed: + return _try_begin_controller_hotbar_placement() + if accept_pressed: + return _try_enter_notepad_controller_ownership() + return false + + +func _event_matches_controller_role( + event: InputEvent, + role: StringName, + fallback_button: JoyButton, +) -> bool: + if _controller_mapping_manager != null: + return _controller_mapping_manager.event_matches_role(event, role) + var button_event := event as InputEventJoypadButton + return button_event != null and button_event.button_index == fallback_button + + +func _try_enter_notepad_controller_ownership() -> bool: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner == null: + return false + var source_identity: StringName + var actions: Array[BaseButton] = [] + if _current_section == Section.COOLER: + var fish_node := focus_owner as CoolerFishSpriteType + if fish_node == null or fish_node.catch_id.is_empty(): + return false + source_identity = fish_node.catch_id + _on_catch_card_pressed(source_identity) + if not _favorite_bubble.disabled: + actions.append(_favorite_bubble) + if not _sell_bubble.disabled: + actions.append(_sell_bubble) + if not _sell_all_bubble.disabled: + actions.append(_sell_all_bubble) + elif _current_section == Section.TACKLE_BOX: + if not focus_owner.has_meta(&"controller_tackle_item_id"): + return false + source_identity = StringName( + str(focus_owner.get_meta(&"controller_tackle_item_id")) + ) + _select_tackle_item(source_identity) + if _tackle_equip_button.visible and not _tackle_equip_button.disabled: + actions.append(_tackle_equip_button) + else: + return false + if actions.is_empty(): + _restore_controller_item_focus(_current_section, source_identity) + return true + _controller_source_section = _current_section + _controller_source_identity = source_identity + _controller_notepad_actions = actions + _controller_ownership = ControllerOwnership.NOTEPAD_ACTIONS + actions.front().call_deferred("grab_focus") + return true + + +func _focus_next_notepad_action(direction: int) -> void: + var available: Array[BaseButton] = [] + for action: BaseButton in _controller_notepad_actions: + if ( + is_instance_valid(action) + and action.visible + and not action.disabled + and action.focus_mode != Control.FOCUS_NONE + ): + available.append(action) + if available.is_empty(): + return + var focused: Control = get_viewport().gui_get_focus_owner() + var current_index: int = available.find(focused) + if current_index < 0: + current_index = 0 if direction > 0 else available.size() - 1 + else: + current_index = wrapi( + current_index + direction, + 0, + available.size(), + ) + available[current_index].grab_focus() + + +func _try_begin_controller_hotbar_placement() -> bool: + if _hotbar == null: + return false + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner == null: + return false + var assignment_kind: PlayerHotbarType.AssignmentKind = ( + PlayerHotbarType.AssignmentKind.EMPTY + ) + var identity: StringName + if _current_section == Section.COOLER: + var fish_node := focus_owner as CoolerFishSpriteType + if fish_node == null or fish_node.catch_id.is_empty(): + return false + assignment_kind = PlayerHotbarType.AssignmentKind.FISH + identity = fish_node.catch_id + elif _current_section == Section.BAG: + var item_node := focus_owner as BagItemSpriteType + if item_node == null or item_node.item_id.is_empty(): + return false + var item: ItemDataType = ( + _item_catalog.get_item_by_id(item_node.item_id) + if _item_catalog != null + else null + ) + if item == null or not item.hotbar_allowed: + return true + assignment_kind = PlayerHotbarType.AssignmentKind.ITEM + identity = item_node.item_id + else: + return false + _controller_source_section = _current_section + _controller_source_identity = identity + _controller_hotbar_assignment_kind = assignment_kind + _controller_hotbar_identity = identity + _controller_previous_hotbar_slot = _hotbar.get_selected_slot() + var initial_slot: int = _find_controller_hotbar_assignment( + assignment_kind, + identity, + ) + if initial_slot < 0: + initial_slot = _controller_previous_hotbar_slot + _controller_ownership = ControllerOwnership.HOTBAR_PLACEMENT + controller_hotbar_placement_requested.emit( + assignment_kind, + identity, + initial_slot, + ) + return true + + +func _find_controller_hotbar_assignment( + assignment_kind: PlayerHotbarType.AssignmentKind, + identity: StringName, +) -> int: + for slot_index: int in range(PlayerHotbarType.SLOT_COUNT): + if ( + assignment_kind == PlayerHotbarType.AssignmentKind.FISH + and _hotbar.get_fish_catch_id(slot_index) == identity + ): + return slot_index + if ( + assignment_kind == PlayerHotbarType.AssignmentKind.ITEM + and _hotbar.get_item_id(slot_index) == identity + ): + return slot_index + return -1 + + +func _confirm_controller_hotbar_placement() -> void: + if ( + _controller_ownership != ControllerOwnership.HOTBAR_PLACEMENT + or _hotbar == null + ): + return + var slot_index: int = _hotbar.get_selected_slot() + var assigned: bool = false + if ( + _controller_hotbar_assignment_kind + == PlayerHotbarType.AssignmentKind.FISH + ): + assigned = _hotbar.assign_fish( + slot_index, + _controller_hotbar_identity, + ) + elif ( + _controller_hotbar_assignment_kind + == PlayerHotbarType.AssignmentKind.ITEM + ): + assigned = _hotbar.assign_item( + slot_index, + _controller_hotbar_identity, + ) + if assigned: + _release_controller_ownership(true, false) + + +func _release_controller_ownership( + restore_source_focus: bool, + restore_previous_hotbar_slot: bool, +) -> void: + if _controller_ownership == ControllerOwnership.ITEM_LIST: + return + var prior_ownership: ControllerOwnership = _controller_ownership + var source_section: Section = _controller_source_section + var source_identity: StringName = _controller_source_identity + _controller_ownership = ControllerOwnership.ITEM_LIST + _controller_notepad_actions.clear() + _controller_source_identity = StringName() + _controller_hotbar_assignment_kind = PlayerHotbarType.AssignmentKind.EMPTY + _controller_hotbar_identity = StringName() + if prior_ownership == ControllerOwnership.HOTBAR_PLACEMENT: + if restore_previous_hotbar_slot and _hotbar != null: + _hotbar.select_slot(_controller_previous_hotbar_slot) + controller_hotbar_placement_ended.emit() + if prior_ownership == ControllerOwnership.HOTBAR_MANAGEMENT: + controller_hotbar_management_ended.emit() + inventory_hotbar_context_changed.emit( + _current_section in [Section.COOLER, Section.BAG] + ) + if restore_source_focus: + call_deferred( + "_restore_controller_item_focus", + source_section, + source_identity, + ) + + +func _restore_controller_item_focus( + section: Section, + identity: StringName, +) -> void: + if not visible or section != _current_section: + return + if identity.is_empty(): + _focus_current_section() + return + var target: Control + if section == Section.COOLER: + target = _fish_nodes.get(identity) as CoolerFishSpriteType + elif section == Section.BAG: + target = _bag_item_nodes.get(identity) as BagItemSpriteType + elif section == Section.TACKLE_BOX: + for child: Node in _tackle_item_list.get_children(): + var button := child as BaseButton + if ( + button != null + and button.has_meta(&"controller_tackle_item_id") + and StringName(str(button.get_meta( + &"controller_tackle_item_id" + ))) == identity + ): + target = button + break + if ( + target != null + and is_instance_valid(target) + and target.is_visible_in_tree() + and target.focus_mode != Control.FOCUS_NONE + ): + target.grab_focus() + else: + _focus_current_section() + + +func _handle_controller_page_switch(event: InputEvent) -> bool: + var button_event: InputEventJoypadButton = event as InputEventJoypadButton + var motion_event: InputEventJoypadMotion = event as InputEventJoypadMotion + var use_mapping: bool = ( + _controller_mapping_manager != null + ) + var uses_left_trigger: bool = ( + _controller_mapping_manager.event_uses_role( + event, ControllerMappingManagerType.ROLE_POINTER_MODIFIER + ) + if use_mapping + else ( + motion_event != null + and motion_event.axis == JOY_AXIS_TRIGGER_LEFT + ) + ) + var uses_right_trigger: bool = ( + _controller_mapping_manager.event_uses_role( + event, ControllerMappingManagerType.ROLE_CAMERA_ZOOM + ) + if use_mapping + else ( + motion_event != null + and motion_event.axis == JOY_AXIS_TRIGGER_RIGHT + ) + ) + if ( + not (uses_left_trigger or uses_right_trigger) + or not visible + ): + return false + var is_pressed: bool = ( + button_event.pressed + if button_event != null + else motion_event.axis_value > 0.5 + ) + if not is_pressed: + if uses_left_trigger: + _left_page_trigger_held = false + if uses_right_trigger: + _right_page_trigger_held = false + return true + if ( + (uses_left_trigger and _left_page_trigger_held) + or (uses_right_trigger and _right_page_trigger_held) + ): + return true + if uses_left_trigger: + _left_page_trigger_held = true + if uses_right_trigger: + _right_page_trigger_held = true + # Trigger input belongs to the Player Menu while it is visible, even when a + # transition or modal temporarily prevents changing pages. + if ( + _transitioning + or _page_transitioning + or _sale_confirmation.visible + or get_viewport().gui_is_dragging() + or _controller_ownership != ControllerOwnership.ITEM_LIST + ): + return true + var sections: Array[Section] = [ + _last_inventory_section, + Section.LOGBOOK, + Section.NET, + Section.MAIL, + Section.PROFILE, + Section.PLAYERS, + ] + var current_index: int = 0 + if not _is_inventory_section(_current_section): + current_index = sections.find(_current_section) + if current_index < 0: + current_index = 0 + var direction: int = ( + -1 if uses_left_trigger else 1 + ) + var next_index: int = wrapi(current_index + direction, 0, sections.size()) + _show_section(sections[next_index]) + return true + + +func _handle_controller_secondary_switch(event: InputEvent) -> bool: + var button_event := event as InputEventJoypadButton + if button_event == null or not visible: + return false + var use_mapping: bool = _controller_mapping_manager != null + var uses_left_bumper: bool = ( + _controller_mapping_manager.event_uses_role( + event, ControllerMappingManagerType.ROLE_LB + ) + if use_mapping + else button_event.button_index == JOY_BUTTON_LEFT_SHOULDER + ) + var uses_right_bumper: bool = ( + _controller_mapping_manager.event_uses_role( + event, ControllerMappingManagerType.ROLE_RB + ) + if use_mapping + else button_event.button_index == JOY_BUTTON_RIGHT_SHOULDER + ) + if not (uses_left_bumper or uses_right_bumper): + return false + if not button_event.pressed: + return true + var direction: int = -1 if uses_left_bumper else 1 + if _controller_ownership == ControllerOwnership.HOTBAR_MANAGEMENT: + _cycle_from_controller_hotbar_management(direction) + return true + if ( + _transitioning + or _page_transitioning + or _sale_confirmation.visible + or get_viewport().gui_is_dragging() + or _controller_ownership != ControllerOwnership.ITEM_LIST + ): + return true + if _is_inventory_section(_current_section): + if ( + (direction > 0 and _current_section == Section.BAG) + or (direction < 0 and _current_section == Section.COOLER) + ): + _begin_controller_hotbar_management() + return true + var inventory_sections: Array[Section] = [ + Section.COOLER, + Section.TACKLE_BOX, + Section.BAG, + ] + var inventory_index: int = inventory_sections.find(_current_section) + _show_section(inventory_sections[wrapi( + inventory_index + direction, + 0, + inventory_sections.size(), + )]) + return true + _cycle_visible_secondary_tabs(direction) + return true + + +func _begin_controller_hotbar_management() -> void: + if _hotbar == null: + return + _controller_source_section = _current_section + _controller_source_identity = StringName() + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if _current_section == Section.COOLER: + var fish_node := focus_owner as CoolerFishSpriteType + if fish_node != null: + _controller_source_identity = fish_node.catch_id + elif _current_section == Section.BAG: + var item_node := focus_owner as BagItemSpriteType + if item_node != null: + _controller_source_identity = item_node.item_id + _controller_ownership = ControllerOwnership.HOTBAR_MANAGEMENT + controller_hotbar_management_requested.emit(_hotbar.get_selected_slot()) + + +func _cycle_from_controller_hotbar_management(direction: int) -> void: + _release_controller_ownership(false, false) + var target_section: Section = ( + Section.COOLER if direction > 0 else Section.BAG + ) + if target_section == _current_section: + call_deferred("_focus_current_section") + else: + _show_section(target_section) + + +func _cycle_visible_secondary_tabs(direction: int) -> bool: + var navigation_cluster := get_node_or_null("%NavigationCluster") as Control + var grouped_buttons: Dictionary = {} + _collect_visible_toggle_buttons(self, navigation_cluster, grouped_buttons) + var selected_group: Variant = null + var selected_group_y: float = INF + for group_value: Variant in grouped_buttons.keys(): + var buttons := grouped_buttons[group_value] as Array + if buttons.size() < 2: + continue + var group_y: float = INF + for item: Variant in buttons: + var button := item as BaseButton + group_y = minf(group_y, button.global_position.y) + if group_y < selected_group_y: + selected_group = group_value + selected_group_y = group_y + if selected_group == null: + return false + var tabs := grouped_buttons[selected_group] as Array + tabs.sort_custom(func(first: BaseButton, second: BaseButton) -> bool: + return first.global_position.x < second.global_position.x + ) + var current_index: int = 0 + for index: int in tabs.size(): + var tab := tabs[index] as BaseButton + if tab.button_pressed: + current_index = index + break + var target := tabs[wrapi( + current_index + direction, 0, tabs.size() + )] as BaseButton + target.set_pressed_no_signal(true) + target.pressed.emit() + return true + + +func _collect_visible_toggle_buttons( + root: Node, + navigation_cluster: Control, + grouped_buttons: Dictionary, +) -> void: + for child: Node in root.get_children(): + var child_control := child as Control + if child_control != null and not child_control.is_visible_in_tree(): + continue + var button := child as BaseButton + if ( + button != null + and button.toggle_mode + and ( + navigation_cluster == null + or not navigation_cluster.is_ancestor_of(button) + ) + ): + var group_key: Variant = button.get_parent() + if button.button_group != null: + group_key = button.button_group + if not grouped_buttons.has(group_key): + grouped_buttons[group_key] = [] + var buttons := grouped_buttons[group_key] as Array + buttons.append(button) + _collect_visible_toggle_buttons( + child, navigation_cluster, grouped_buttons + ) + + func _handle_direct_page_shortcut(event: InputEvent) -> bool: var key_event := event as InputEventKey if ( @@ -667,6 +1300,7 @@ func open_menu() -> void: or not _fishing_spot.can_open_player_menu() ): return + _release_controller_ownership(false, true) _menu_generation += 1 _transition_generation += 1 _cancel_presentation_tween() @@ -686,6 +1320,28 @@ func open_menu() -> void: _update_shell_layout() _begin_menu_entry() menu_visibility_changed.emit(true) + inventory_hotbar_context_changed.emit( + _current_section in [Section.COOLER, Section.BAG] + ) + + +func open_section(section: Section) -> bool: + if ( + _shop_cooler_context_active + or _player == null + or _fishing_spot == null + or not _fishing_spot.can_open_player_menu() + ): + return false + if visible: + if _transitioning or _page_transitioning or _sale_confirmation.visible: + return false + if section != _current_section: + _show_section(section) + return true + _current_section = section + open_menu() + return visible func mount_shop_cooler( @@ -723,7 +1379,7 @@ func mount_shop_cooler( _set_content_interactive(true) _update_cooler_water_mask() set_process(true) - call_deferred("_focus_shop_cooler") + _cooler_sort_option.call_deferred("grab_focus") return true @@ -797,6 +1453,7 @@ func close_menu( if reason != CloseReason.USER: _finish_close(reason, restore_controls, _menu_generation) return + _release_controller_ownership(false, true) get_viewport().gui_cancel_drag() _close_sale_confirmation() if reason in [ @@ -825,6 +1482,7 @@ func close_for_session_end() -> void: func _exit_tree() -> void: + _release_controller_ownership(false, true) _cancel_presentation_tween() _cancel_page_tween() if visible: @@ -883,6 +1541,7 @@ func _show_section(section: Section) -> void: and _profile_page.request_close_confirmation() ): return + _release_controller_ownership(false, true) _begin_page_transition(section) @@ -898,6 +1557,7 @@ func _show_section_immediate(section: Section) -> void: _tackle_box_page.visible = section == Section.TACKLE_BOX _inventory_sub_tabs.visible = _is_inventory_section(section) _logbook_page.visible = section == Section.LOGBOOK + _the_net_page.visible = section == Section.NET _mail_page.visible = section == Section.MAIL _profile_page.visible = section == Section.PROFILE _players_page.visible = section == Section.PLAYERS @@ -922,6 +1582,10 @@ func _show_section_immediate(section: Section) -> void: _catalog_logbook.deactivate() else: _catalog_logbook.activate() + if section == Section.NET: + _the_net_page.activate() + else: + _the_net_page.deactivate() if section == Section.MAIL: _mail_page.activate() else: @@ -940,6 +1604,7 @@ func _show_section_immediate(section: Section) -> void: _tackle_sub_tab.set_selected(section == Section.TACKLE_BOX) _refresh_inventory_organizer_tabs() _logbook_tab.button_pressed = section == Section.LOGBOOK + _the_net_tab.button_pressed = section == Section.NET _mail_tab.button_pressed = section == Section.MAIL _profile_tab.button_pressed = section == Section.PROFILE _players_tab.button_pressed = section == Section.PLAYERS @@ -992,20 +1657,39 @@ func _focus_current_section() -> void: if _shop_cooler_context_active: _focus_shop_cooler() return - if _current_section == Section.COOLER: - _cooler_sub_tab.grab_focus() - elif _current_section == Section.BAG: - _bag_sub_tab.grab_focus() - elif _current_section == Section.TACKLE_BOX: - _tackle_sub_tab.grab_focus() - elif _current_section == Section.LOGBOOK: - _catalog_logbook.focus_initial() - elif _current_section == Section.MAIL: - _mail_tab.grab_focus() - elif _current_section == Section.PLAYERS: - _players_tab.grab_focus() - else: - _profile_tab.grab_focus() + _reserve_main_navigation_for_page_switching() + _reserve_visible_secondary_navigation() + var navigation_cluster := get_node_or_null("%NavigationCluster") as Control + var candidates: Array[Control] = [] + _collect_focusable_content(self, navigation_cluster, candidates) + if candidates.is_empty(): + return + candidates.sort_custom(func(first: Control, second: Control) -> bool: + if not is_equal_approx(first.global_position.y, second.global_position.y): + return first.global_position.y < second.global_position.y + return first.global_position.x < second.global_position.x + ) + candidates[0].grab_focus() + + +func _collect_focusable_content( + root: Node, + navigation_cluster: Control, + output: Array[Control], +) -> void: + for child: Node in root.get_children(): + if child == navigation_cluster: + continue + var control := child as Control + if control != null and not control.is_visible_in_tree(): + continue + if ( + control != null + and control.focus_mode != Control.FOCUS_NONE + and not control is ScrollBar + ): + output.append(control) + _collect_focusable_content(child, navigation_cluster, output) func _process(delta: float) -> void: @@ -1029,9 +1713,11 @@ func _process(delta: float) -> void: func _configure_navigation_focus() -> void: + _reserve_main_navigation_for_page_switching() var navigation: Array[BubbleButtonType] = [ _inventory_tab, _logbook_tab, + _the_net_tab, _mail_tab, _profile_tab, _players_tab, @@ -1049,6 +1735,48 @@ func _configure_navigation_focus() -> void: bubble.focus_neighbor_right = bubble.get_path_to(next) bubble.focus_neighbor_top = bubble.focus_neighbor_left bubble.focus_neighbor_bottom = bubble.focus_neighbor_right + + +func _reserve_main_navigation_for_page_switching() -> void: + var navigation_cluster := get_node_or_null("%NavigationCluster") as Control + if navigation_cluster == null: + return + _set_descendant_focus_disabled(navigation_cluster) + + +func _set_descendant_focus_disabled(root: Node) -> void: + for child: Node in root.get_children(): + var control := child as Control + if control != null: + control.focus_mode = Control.FOCUS_NONE + _set_descendant_focus_disabled(child) + + +func _reserve_visible_secondary_navigation() -> void: + var navigation_cluster := get_node_or_null("%NavigationCluster") as Control + var grouped_buttons: Dictionary = {} + _collect_visible_toggle_buttons(self, navigation_cluster, grouped_buttons) + var selected_tabs: Array = [] + var selected_group_y: float = INF + for group_value: Variant in grouped_buttons.keys(): + var buttons := grouped_buttons[group_value] as Array + if buttons.size() < 2: + continue + var group_y: float = INF + for item: Variant in buttons: + var button := item as BaseButton + group_y = minf(group_y, button.global_position.y) + if group_y < selected_group_y: + selected_tabs = buttons + selected_group_y = group_y + var focus_owner: Control = get_viewport().gui_get_focus_owner() + var displaced_focus: bool = selected_tabs.has(focus_owner) + for item: Variant in selected_tabs: + var tab := item as Control + tab.focus_mode = Control.FOCUS_NONE + if displaced_focus: + focus_owner.release_focus() + call_deferred("_focus_current_section") var inventory_tabs: Array[Button] = [ _cooler_sub_tab, _tackle_sub_tab, @@ -1122,6 +1850,8 @@ func _configure_active_page_focus() -> void: _bait_filter.grab_focus() Section.LOGBOOK: _catalog_logbook.focus_initial() + Section.NET: + _the_net_page.focus_initial() Section.MAIL: _mail_page.activate() Section.PROFILE: @@ -1154,11 +1884,11 @@ func _apply_inventory_styles() -> void: UtilityPageStyle.panel_style(), ) _confirmation_message.add_theme_font_override( - "font", + "normal_font", UtilityPageStyle.TuffyFont, ) _confirmation_message.add_theme_color_override( - "font_color", + "default_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY, ) UtilityPageStyle.apply_ocean_button(_confirm_sale_button) @@ -1188,7 +1918,12 @@ func _refresh_tackle_box() -> void: child.queue_free() var matching_items: Array[OwnedItemType] = [] if _bag != null and _item_catalog != null: - for owned: OwnedItemType in _bag.get_all_items(): + var available_items: Array[OwnedItemType] = ( + _bag.get_unlocked_bait_items() + if _tackle_view == TackleView.BAIT + else _bag.get_all_items() + ) + for owned: OwnedItemType in available_items: var item: ItemDataType = _item_catalog.get_item_by_id( owned.item_id ) @@ -1208,12 +1943,33 @@ func _refresh_tackle_box() -> void: for owned: OwnedItemType in matching_items: var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id) var row := Button.new() - row.text = "%s ×%d" % [item.display_name, owned.quantity] row.icon = item.icon - row.alignment = HORIZONTAL_ALIGNMENT_LEFT + row.tooltip_text = ( + "%s ×%d" % [item.display_name, owned.quantity] + if item.is_bait() + else item.display_name + ) + var compact_tackle_item: bool = item.is_bait() or item.is_lure() + if compact_tackle_item: + row.custom_minimum_size = Vector2(72, 72) + row.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + row.expand_icon = item.icon != null + row.alignment = HORIZONTAL_ALIGNMENT_CENTER + row.text = "" if item.icon != null else item.display_name + if item.icon == null: + row.add_theme_font_size_override("font_size", 12) + else: + row.text = "%s ×%d" % [item.display_name, owned.quantity] + row.alignment = HORIZONTAL_ALIGNMENT_LEFT row.toggle_mode = true + row.set_meta(&"controller_tackle_item_id", owned.item_id) row.button_pressed = owned.item_id == _selected_tackle_item_id - UtilityPageStyle.apply_ocean_button(row) + if compact_tackle_item: + _apply_tackle_bait_button_style(row) + if item.is_bait(): + _add_tackle_quantity_badge(row, owned.quantity) + else: + UtilityPageStyle.apply_ocean_button(row) row.pressed.connect(_select_tackle_item.bind(owned.item_id)) _tackle_item_list.add_child(row) _tackle_empty.text = ( @@ -1225,6 +1981,68 @@ func _refresh_tackle_box() -> void: _update_tackle_detail() +func _add_tackle_quantity_badge(row: Button, quantity: int) -> void: + var badge := Panel.new() + badge.name = "QuantityBadge" + badge.anchor_left = 1.0 + badge.anchor_right = 1.0 + badge.offset_left = -27.0 + badge.offset_top = 3.0 + badge.offset_right = -3.0 + badge.offset_bottom = 27.0 + badge.mouse_filter = Control.MOUSE_FILTER_IGNORE + badge.z_index = 2 + var badge_style := StyleBoxFlat.new() + badge_style.bg_color = Color("0b5558") + badge_style.set_corner_radius_all(12) + badge_style.anti_aliasing = false + badge.add_theme_stylebox_override("panel", badge_style) + row.add_child(badge) + var quantity_label := Label.new() + quantity_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + quantity_label.offset_left = 1.0 + quantity_label.offset_right = 1.0 + quantity_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + quantity_label.text = str(quantity) + quantity_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + quantity_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + quantity_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) + quantity_label.add_theme_font_size_override( + "font_size", + 13 if quantity >= 100 else 15, + ) + quantity_label.add_theme_color_override("font_color", Color("e7f5f4")) + badge.add_child(quantity_label) + + +func _apply_tackle_bait_button_style(button: Button) -> void: + var profile := BubbleMenuProfile.new() + var normal_style: StyleBoxFlat = profile.make_normal_style() + var hover_style: StyleBoxFlat = profile.make_hover_style() + var pressed_style: StyleBoxFlat = profile.make_pressed_style() + var disabled_style: StyleBoxFlat = profile.make_disabled_style() + for style: StyleBoxFlat in [ + normal_style, + hover_style, + pressed_style, + disabled_style, + ]: + style.set_corner_radius_all(36) + style.content_margin_left = 13.5 + style.content_margin_top = 13.5 + style.content_margin_right = 13.5 + style.content_margin_bottom = 13.5 + button.add_theme_stylebox_override("normal", normal_style) + button.add_theme_stylebox_override("hover", hover_style) + button.add_theme_stylebox_override("focus", hover_style) + button.add_theme_stylebox_override("pressed", pressed_style) + button.add_theme_stylebox_override("disabled", disabled_style) + button.add_theme_color_override("icon_normal_color", Color.WHITE) + button.add_theme_color_override("icon_hover_color", Color.WHITE) + button.add_theme_color_override("icon_focus_color", Color.WHITE) + button.add_theme_color_override("icon_pressed_color", Color.WHITE) + + func _select_tackle_item(item_id: StringName) -> void: _selected_tackle_item_id = item_id _refresh_tackle_box() @@ -1237,12 +2055,15 @@ func _update_tackle_detail() -> void: else null ) if item == null: + _tackle_equip_button.visible = false + _tackle_equip_button.persistent_mark = false _tackle_detail_text.text = ( "Select bait for details." if _tackle_view == TackleView.BAIT else "Select a lure for details." ) return + _tackle_equip_button.visible = item.is_bait() or item.is_lure() var quantity: int = _bag.get_quantity(item.item_id) if _bag != null else 0 var assigned_slot: int = -1 if _hotbar != null: @@ -1250,21 +2071,60 @@ func _update_tackle_detail() -> void: if _hotbar.get_item_id(slot_index) == item.item_id: assigned_slot = slot_index break - var state_text: String = ( - "hotbar slot %d" % (assigned_slot + 1) - if assigned_slot >= 0 - else "not assigned" - ) - _tackle_detail_text.text = ( - "%s\n\nType: %s\nQuantity: %d\n%s\n\n%s" - % [ - item.display_name, - item.get_category_name(), - quantity, - state_text, - item.description, - ] - ) + var detail_lines: Array[String] = [item.display_name, ""] + if item.is_bait(): + detail_lines.append("quantity: %d" % quantity) + if assigned_slot >= 0: + detail_lines.append("hotbar slot %d" % (assigned_slot + 1)) + detail_lines.append("") + detail_lines.append(item.description) + _tackle_detail_text.text = "\n".join(detail_lines) + if item.is_bait() or item.is_lure(): + var is_equipped: bool = ( + _player != null + and ( + _player.active_bait_id == item.item_id + if item.is_bait() + else _player.active_lure_id == item.item_id + ) + ) + _tackle_equip_button.text = ( + "dequip %s" % item.display_name + if is_equipped + else "equip %s" % item.display_name + ) + _tackle_equip_button.persistent_mark = is_equipped + _tackle_equip_button.disabled = quantity <= 0 + _tackle_equip_button.refresh_ink_state() + + +func _toggle_active_tackle() -> void: + if _player == null or _item_catalog == null: + return + var item: ItemDataType = _item_catalog.get_item_by_id(_selected_tackle_item_id) + if item == null: + return + if item.is_bait(): + if _player.active_bait_id == item.item_id: + _player.unequip_bait() + else: + _player.equip_bait(item) + elif item.is_lure(): + if _player.active_lure_id == item.item_id: + _player.unequip_lure() + else: + _player.equip_lure(item) + else: + return + _update_tackle_detail() + + +func _on_active_bait_changed(_item_id: StringName) -> void: + _update_tackle_detail() + + +func _on_active_lure_changed(_item_id: StringName) -> void: + _update_tackle_detail() func _apply_cooler_control_styles() -> void: @@ -1349,6 +2209,7 @@ func _apply_navigation_styles() -> void: for bubble: BubbleButtonType in [ _inventory_tab, _logbook_tab, + _the_net_tab, _mail_tab, _profile_tab, _players_tab, @@ -1380,6 +2241,7 @@ func _apply_navigation_selection_presentation() -> void: for bubble: BubbleButtonType in [ _inventory_tab, _logbook_tab, + _the_net_tab, _mail_tab, _profile_tab, _players_tab, @@ -1465,6 +2327,7 @@ func _on_mail_unread_count_changed(count: int) -> void: func _set_navigation_target(section: Section) -> void: _inventory_tab.button_pressed = _is_inventory_section(section) _logbook_tab.button_pressed = section == Section.LOGBOOK + _the_net_tab.button_pressed = section == Section.NET _mail_tab.button_pressed = section == Section.MAIL _profile_tab.button_pressed = section == Section.PROFILE _players_tab.button_pressed = section == Section.PLAYERS @@ -1528,10 +2391,10 @@ func _update_shell_layout() -> void: _cooler_sort_direction.custom_minimum_size = ( Vector2(140.0, 31.0) if compact else Vector2(123.0, 38.0) ) - _cooler_sort_option.set_choice_font_size(12 if compact else 15) + _cooler_sort_option.set_choice_font_size(14 if compact else 17) _cooler_sort_direction.add_theme_font_size_override( "font_size", - 12 if compact else 14, + 14 if compact else 17, ) _notepad_wallet_value.position = ( Vector2(10.0, 33.0) if compact else Vector2(18.0, 61.0) @@ -1545,14 +2408,13 @@ func _update_shell_layout() -> void: _notepad_capacity_value.size = ( Vector2(142.0, 17.0) if compact else Vector2(124.0, 23.0) ) - for status_value: Label in [ - _notepad_wallet_value, - _notepad_capacity_value, - ]: - status_value.add_theme_font_size_override( - "font_size", - 10 if compact else 14, - ) + _notepad_wallet_value.icon_size = 10.0 if compact else 14.0 + _notepad_wallet_value.get_amount_label().add_theme_font_size_override( + "font_size", 10 if compact else 14 + ) + _notepad_capacity_value.add_theme_font_size_override( + "font_size", 10 if compact else 14 + ) _cooler_detail_texture.position = ( Vector2(5.0, 84.0) if compact else Vector2(80.0, 137.0) ) @@ -1588,6 +2450,9 @@ func _update_shell_layout() -> void: _sell_bubble.custom_minimum_size = ( Vector2(74.0, 42.0) if compact else Vector2(118.0, 50.0) ) + _sell_all_bubble.custom_minimum_size = ( + Vector2(96.0, 38.0) if compact else Vector2(122.0, 44.0) + ) _favorite_bubble.position = ( Vector2(144.0, 148.0) if compact else Vector2(26.0, 378.0) ) @@ -1600,11 +2465,20 @@ func _update_shell_layout() -> void: _sell_bubble.size = ( Vector2(74.0, 42.0) if compact else Vector2(118.0, 50.0) ) + _sell_all_bubble.position = ( + Vector2(102.0, 190.0) if compact else Vector2(78.0, 426.0) + ) + _sell_all_bubble.size = ( + Vector2(96.0, 38.0) if compact else Vector2(122.0, 44.0) + ) _favorite_bubble.add_theme_font_size_override( - "font_size", 11 if compact else 17, + "font_size", 13 if compact else 19, ) _sell_bubble.add_theme_font_size_override( - "font_size", 11 if compact else 17, + "font_size", 13 if compact else 19, + ) + _sell_all_bubble.add_theme_font_size_override( + "font_size", 12 if compact else 17, ) _update_sort_direction_text() _bag_page.size = reference_size @@ -1643,14 +2517,18 @@ func _update_shell_layout() -> void: _logbook_page.size = reference_size _logbook_page.position = Vector2.ZERO _logbook_rest_position = Vector2.ZERO + _the_net_page.set_anchors_preset(Control.PRESET_TOP_LEFT) + _the_net_page.size = reference_size + _the_net_page.position = Vector2.ZERO + _the_net_rest_position = Vector2.ZERO _mail_page.set_anchors_preset(Control.PRESET_TOP_LEFT) _mail_page.size = reference_size _mail_page.position = Vector2.ZERO _mail_rest_position = Vector2.ZERO _profile_page.set_anchors_preset(Control.PRESET_TOP_LEFT) - _profile_page.position = Vector2(42.0, 104.0) - _profile_page.size = Vector2(1196.0, 608.0) - _profile_rest_position = _profile_page.position + _profile_page.position = Vector2.ZERO + _profile_page.size = reference_size + _profile_rest_position = Vector2.ZERO _players_page.set_anchors_preset(Control.PRESET_TOP_LEFT) _players_page.position = Vector2.ZERO _players_page.size = reference_size @@ -1673,6 +2551,7 @@ func _update_shell_layout() -> void: _bag_page.position = _bag_rest_position _tackle_box_page.position = _tackle_rest_position _logbook_page.position = _logbook_rest_position + _the_net_page.position = _the_net_rest_position _mail_page.position = _mail_rest_position _profile_page.position = _profile_rest_position _players_page.position = Vector2.ZERO @@ -1680,6 +2559,7 @@ func _update_shell_layout() -> void: _bag_page.modulate.a = 1.0 _tackle_box_page.modulate.a = 1.0 _logbook_page.modulate.a = 1.0 + _the_net_page.modulate.a = 1.0 _mail_page.modulate.a = 1.0 _profile_page.modulate.a = 1.0 _players_page.modulate.a = 1.0 @@ -1716,24 +2596,25 @@ func _layout_cooler_detail_text(compact: bool) -> void: "font_size", 13 if compact else 18, ) - _cooler_offer_value.add_theme_font_size_override( - "font_size", - 10 if compact else 15, + _cooler_offer_value.icon_size = 10.0 if compact else 15.0 + _cooler_offer_value.get_amount_label().add_theme_font_size_override( + "font_size", 10 if compact else 15 ) func _layout_cooler_selection_text(compact: bool) -> void: var row_height: float = 17.5 if compact else 20.0 - for numeric_label: Label in [ - _cooler_selected_count_value, - _cooler_combined_offer_value, - ]: - numeric_label.add_theme_font_size_override( - "font_size", - 10 if compact else 14, - ) + _cooler_selected_count_value.add_theme_font_size_override( + "font_size", 10 if compact else 14 + ) + _cooler_combined_offer_value.icon_size = 10.0 if compact else 14.0 + _cooler_combined_offer_value.get_amount_label().add_theme_font_size_override( + "font_size", 10 if compact else 14 + ) + _cooler_selection_empty.add_theme_font_size_override( + "font_size", 14 if compact else 20 + ) for word_label: Label in [ - _cooler_selection_empty, _cooler_selected_count_label, _cooler_combined_offer_label, ]: @@ -1805,7 +2686,11 @@ func _begin_menu_exit(reason: CloseReason, restore_controls: bool) -> void: _settle_inventory_tabs_for_close() if _current_section == Section.LOGBOOK: _catalog_logbook.deactivate() - get_viewport().gui_release_focus() + elif _current_section == Section.NET: + _the_net_page.deactivate() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() var generation: int = _transition_generation var closing_generation: int = _menu_generation _presentation_tween = create_tween().set_parallel(true) @@ -1851,6 +2736,7 @@ func _finish_close( _cancel_presentation_tween() _cancel_page_tween() _cancel_logbook_page_transition(true) + _the_net_page.deactivate() _transitioning = false _page_transitioning = false _bag_drag_active = false @@ -1859,7 +2745,9 @@ func _finish_close( _presentation_scale_root.scale = Vector2.ONE visible = false set_process(false) - get_viewport().gui_release_focus() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() if _fishing_spot != null and is_instance_valid(_fishing_spot): _fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false) if restore_controls: @@ -1882,6 +2770,8 @@ func _get_section_root(section: Section) -> Control: return _tackle_box_page Section.LOGBOOK: return _logbook_page + Section.NET: + return _the_net_page Section.MAIL: return _mail_page Section.PROFILE: @@ -1900,6 +2790,8 @@ func _get_section_rest_position(section: Section) -> Vector2: return _tackle_rest_position Section.LOGBOOK: return _logbook_rest_position + Section.NET: + return _the_net_rest_position Section.MAIL: return _mail_rest_position Section.PROFILE: @@ -1914,7 +2806,9 @@ func _begin_page_transition(section: Section) -> void: _cancel_page_tween() _page_transitioning = true _set_content_interactive(false) - get_viewport().gui_release_focus() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() var outgoing_section: Section = _current_section var outgoing_inventory: bool = _is_inventory_section(outgoing_section) var incoming_inventory: bool = _is_inventory_section(section) @@ -2002,6 +2896,7 @@ func _set_shell_interactive(interactive: bool) -> void: for bubble: BubbleButtonType in [ _inventory_tab, _logbook_tab, + _the_net_tab, _mail_tab, _profile_tab, _players_tab, @@ -2058,6 +2953,9 @@ func _set_content_interactive(interactive: bool) -> void: _catalog_logbook.set_interactive( interactive and _current_section == Section.LOGBOOK ) + _the_net_page.set_interactive( + interactive and _current_section == Section.NET + ) _mail_page.set_interactive( interactive and _current_section == Section.MAIL ) @@ -2085,7 +2983,11 @@ func _set_content_interactive(interactive: bool) -> void: var detail_interactive: bool = ( cooler_interactive and _detail_constellation.visible ) - for action: NotepadInkActionType in [_favorite_bubble, _sell_bubble]: + for action: NotepadInkActionType in [ + _favorite_bubble, + _sell_bubble, + _sell_all_bubble, + ]: var action_interactive: bool = detail_interactive and not action.disabled action.focus_mode = ( Control.FOCUS_ALL @@ -2133,6 +3035,7 @@ func _reset_page_transition_visuals() -> void: Section.BAG, Section.TACKLE_BOX, Section.LOGBOOK, + Section.NET, Section.MAIL, Section.PROFILE, Section.PLAYERS, @@ -2160,7 +3063,7 @@ func _on_sort_selected(index: int, source: OptionButton) -> void: func _on_cooler_sort_selected(sort_id: int) -> void: - _sort_mode = sort_id + _sort_mode = sort_id as SortMode _sort_option.select(_sort_mode) _cooler_sort_option.select(_sort_mode) _refresh_inventory() @@ -2447,7 +3350,7 @@ func _update_bag_detail() -> void: item_state = "equippable" elif item.usable: item_state = "usable" - _bag_detail_texture.texture = item.icon if item != null else null + _bag_detail_texture.texture = null _bag_detail_name.text = item.display_name if item != null else "" _bag_detail_data.text = ( "%s\nquantity: %d\n%s\n%s\n%s" @@ -2461,7 +3364,10 @@ func _update_bag_detail() -> void: if item != null else "select a bag item for details." ) - _bag_sprite_detail_texture.texture = item.icon if item != null else null + _bag_sprite_detail_texture.texture = null + if item != null: + _bag_detail_texture.texture = item.icon + _bag_sprite_detail_texture.texture = item.icon _bag_sprite_detail_name.text = ( item.display_name if item != null else "" ) @@ -2535,9 +3441,9 @@ func _refresh_economy_summary() -> void: if _inventory != null else 0 ) - _wallet_balance.text = "wallet: $%d" % balance - _wallet_status.set_content("wallet", "$%d" % balance) - _notepad_wallet_value.text = "$%d" % balance + _wallet_balance.set_amount(balance) + _wallet_status.set_currency_amount("wallet", balance) + _notepad_wallet_value.set_amount(balance) _capacity_status.set_content("cooler", "%d / %d" % [ _inventory.get_all_catches().size() if _inventory != null else 0, _cooler_capacity.get_capacity() if _cooler_capacity != null else 0, @@ -2546,8 +3452,8 @@ func _refresh_economy_summary() -> void: _inventory.get_all_catches().size() if _inventory != null else 0, _cooler_capacity.get_capacity() if _cooler_capacity != null else 0, ] - _held_value_status.set_content("held value", "$%d" % held_total) - _held_value.text = "held fish base value: $%d" % held_total + _held_value_status.set_currency_amount("held value", held_total) + _held_value.set_amount(held_total) _cooler_count.text = "%d / %d" % [ _inventory.get_all_catches().size() if _inventory != null else 0, _cooler_capacity.get_capacity() if _cooler_capacity != null else 0, @@ -2569,7 +3475,6 @@ func _refresh_inventory() -> void: catches.sort_custom(_compare_catches) _sorted_catches = catches _inventory_empty.visible = catches.is_empty() - _cooler_empty.visible = catches.is_empty() var visible_ids: Array[StringName] = [] for fish_catch: FishCatchType in catches: visible_ids.append(fish_catch.catch_id) @@ -2706,11 +3611,11 @@ func _configure_cooler_fish_focus() -> void: if not _shop_cooler_context_active: _cooler_sub_tab.focus_neighbor_bottom = NodePath() return - var top_control: Control = ( - _cooler_sort_option - if _shop_cooler_context_active - else _cooler_sub_tab - ) + var top_control: Control + if _shop_cooler_context_active: + top_control = _cooler_sort_option + else: + top_control = _cooler_sub_tab if not _shop_cooler_context_active: _cooler_sub_tab.focus_neighbor_bottom = _cooler_sub_tab.get_path_to( controls.front() @@ -2734,6 +3639,11 @@ func _configure_cooler_fish_focus() -> void: not _favorite_bubble.disabled and index + columns >= controls.size() ) + else control.get_path_to(_sell_all_bubble) + if ( + not _sell_all_bubble.disabled + and index + columns >= controls.size() + ) else control.get_path_to( controls[mini(index + columns, controls.size() - 1)] ) @@ -2759,6 +3669,18 @@ func _configure_cooler_fish_focus() -> void: _sell_bubble.get_path_to(_close_button) ) _sell_bubble.focus_neighbor_top = _sell_bubble.get_path_to(focused_node) + _sell_bubble.focus_neighbor_bottom = ( + _sell_bubble.get_path_to(_sell_all_bubble) + ) + _sell_all_bubble.focus_neighbor_left = ( + _sell_all_bubble.get_path_to(_favorite_bubble) + ) + _sell_all_bubble.focus_neighbor_right = ( + _sell_all_bubble.get_path_to(_close_button) + ) + _sell_all_bubble.focus_neighbor_top = ( + _sell_all_bubble.get_path_to(_sell_bubble) + ) func _compare_catches(left: FishCatchType, right: FishCatchType) -> bool: @@ -2788,7 +3710,7 @@ func _on_catch_card_pressed(catch_id: StringName) -> void: Input.is_key_pressed(KEY_CTRL), Input.is_key_pressed(KEY_SHIFT) ) - _transaction_feedback.text = "" + _set_transaction_feedback("") _refresh_inventory() @@ -2800,7 +3722,7 @@ func _on_fish_field_gui_input(event: InputEvent) -> void: and not get_viewport().gui_is_dragging() ): _fish_selection.clear() - _transaction_feedback.text = "" + _set_transaction_feedback("") _refresh_inventory() @@ -2823,6 +3745,7 @@ func _close_detail_constellation() -> void: _sell_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE _favorite_bubble.refresh_ink_state() _sell_bubble.refresh_ink_state() + _refresh_cooler_notepad_action_interactivity() _configure_cooler_fish_focus() @@ -2831,9 +3754,9 @@ func _clear_cooler_detail_stats() -> void: _cooler_weight_value, _cooler_weight_unit, _cooler_offer_label, - _cooler_offer_value, ]: label.text = "" + _cooler_offer_value.visible = false func _set_cooler_selection_summary_empty() -> void: @@ -2844,10 +3767,10 @@ func _set_cooler_selection_summary_empty() -> void: _cooler_selected_count_value, _cooler_selected_count_label, _cooler_combined_offer_label, - _cooler_combined_offer_value, ]: label.visible = false label.text = "" + _cooler_combined_offer_value.visible = false func _set_cooler_selection_summary( @@ -2865,10 +3788,9 @@ func _set_cooler_selection_summary( _cooler_selected_count_label.text = "fish selected" if offer_value >= 0: _cooler_combined_offer_label.text = "combined offer" - _cooler_combined_offer_value.text = "$%d" % offer_value + _cooler_combined_offer_value.set_amount(offer_value) else: _cooler_combined_offer_label.text = "offer unavailable" - _cooler_combined_offer_value.text = "" func _update_inventory_detail(fish_catch: FishCatchType) -> void: @@ -2889,6 +3811,7 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void: _sell_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE _favorite_bubble.refresh_ink_state() _sell_bubble.refresh_ink_state() + _refresh_cooler_notepad_action_interactivity() _configure_cooler_fish_focus() return _cooler_detail_texture.texture = fish_catch.fish.display_texture @@ -2917,10 +3840,11 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void: ] if buyer_offer >= 0 and active_buyer != null: _cooler_offer_label.text = _get_offer_label(active_buyer) - _cooler_offer_value.text = "$%d" % buyer_offer + _cooler_offer_value.visible = true + _cooler_offer_value.set_amount(buyer_offer) else: _cooler_offer_label.text = "buyer unavailable" - _cooler_offer_value.text = "" + _cooler_offer_value.visible = false _favorite_button.disabled = false _favorite_button.text = ( "unfavorite" if fish_catch.is_favorited else "favorite" @@ -2931,25 +3855,32 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void: _favorite_bubble.refresh_ink_state() _detail_constellation.visible = _current_section == Section.COOLER _cooler_sort_controls.visible = true + _refresh_cooler_notepad_action_interactivity() + _configure_cooler_fish_focus() + + +func _refresh_cooler_notepad_action_interactivity() -> void: var detail_interactive: bool = ( _detail_constellation.visible + and _current_section == Section.COOLER + and (visible or _shop_cooler_context_active) and not _transitioning and not _page_transitioning + and not _sale_confirmation.visible ) - for action: NotepadInkActionType in [_favorite_bubble, _sell_bubble]: + for action: NotepadInkActionType in [ + _favorite_bubble, + _sell_bubble, + _sell_all_bubble, + ]: var action_interactive: bool = detail_interactive and not action.disabled - action.focus_mode = ( - Control.FOCUS_ALL - if action_interactive - else Control.FOCUS_NONE - ) - action.mouse_filter = ( - Control.MOUSE_FILTER_STOP - if action_interactive - else Control.MOUSE_FILTER_IGNORE - ) + if action_interactive: + action.focus_mode = Control.FOCUS_ALL + action.mouse_filter = Control.MOUSE_FILTER_STOP + else: + action.focus_mode = Control.FOCUS_NONE + action.mouse_filter = Control.MOUSE_FILTER_IGNORE action.refresh_ink_state() - _configure_cooler_fish_focus() func _update_sale_summary() -> void: @@ -2957,6 +3888,7 @@ func _update_sale_summary() -> void: var buyer_id: StringName = ( active_buyer.id if active_buyer != null else StringName() ) + _update_sell_all_action(active_buyer, buyer_id) var offer_label: String = _get_offer_label(active_buyer) var selected_ids: Array[StringName] = _fish_selection.get_selected_ids() var selected_count: int = selected_ids.size() @@ -3041,12 +3973,8 @@ func _update_sale_summary() -> void: or preview.status == FishSaleResultType.Status.FAVORITED ) ): - _selection_summary.text += "\n%s total offer: $%d" % [ - active_buyer.display_name, - preview.payout, - ] _set_cooler_selection_summary(selected_count, preview.payout) - _offer_status.set_content(offer_label, "$%d" % preview.payout) + _offer_status.set_currency_amount(offer_label, preview.payout) else: _set_cooler_selection_summary(selected_count, -1) _offer_status.set_content(offer_label, "unavailable") @@ -3066,6 +3994,55 @@ func _update_sale_summary() -> void: _sale_unavailable.visible = not _sale_unavailable.text.is_empty() +func _update_sell_all_action( + active_buyer: FishBuyerProfileType, + buyer_id: StringName, +) -> void: + var sell_all_ids: Array[StringName] = _get_sell_all_catch_ids() + var sale_available: bool = ( + not _sale_in_progress + and ( + _network_sale_service == null + or not _network_sale_service.is_local_sale_pending() + ) + and _network_sale_service != null + and not buyer_id.is_empty() + and _network_sale_service.can_request_sale(buyer_id) + and _sale_service != null + and active_buyer != null + and not sell_all_ids.is_empty() + ) + if sale_available: + var preview: FishSaleResultType = _sale_service.preview_batch( + sell_all_ids, + active_buyer, + ) + sale_available = preview.is_success() + _sell_all_bubble.text = "sell all fish" + _sell_all_bubble.disabled = not sale_available + _sell_all_bubble.persistent_mark = false + _sell_all_bubble.refresh_ink_state() + + +func _get_sell_all_catch_ids() -> Array[StringName]: + var catch_ids: Array[StringName] = [] + if _inventory == null: + return catch_ids + for fish_catch: FishCatchType in _inventory.get_all_catches(): + if ( + fish_catch == null + or not fish_catch.is_valid() + or fish_catch.is_favorited + or ( + _reservations != null + and _reservations.is_fish_reserved(fish_catch.catch_id) + ) + ): + continue + catch_ids.append(fish_catch.catch_id) + return catch_ids + + func _on_favorite_pressed() -> void: var focused_id: StringName = _fish_selection.get_focused_id() if _inventory == null or focused_id.is_empty(): @@ -3080,7 +4057,7 @@ func _on_favorite_pressed() -> void: fish_catch.catch_id, not fish_catch.is_favorited ): - _transaction_feedback.text = ( + _set_transaction_feedback( "%s %s." % [ fish_catch.fish.display_name, @@ -3090,6 +4067,18 @@ func _on_favorite_pressed() -> void: func _on_sell_pressed() -> void: + _begin_sale_confirmation(_fish_selection.get_selected_ids()) + + +func _on_sell_all_pressed() -> void: + var catch_ids: Array[StringName] = _get_sell_all_catch_ids() + if catch_ids.is_empty(): + _set_transaction_feedback("No sellable fish in the cooler.") + return + _begin_sale_confirmation(catch_ids) + + +func _begin_sale_confirmation(catch_ids: Array[StringName]) -> void: var active_buyer: FishBuyerProfileType = _get_active_sale_buyer() var buyer_id: StringName = ( active_buyer.id if active_buyer != null else StringName() @@ -3101,31 +4090,30 @@ func _on_sell_pressed() -> void: and _network_sale_service.is_local_sale_pending() ) ): - _transaction_feedback.text = "Selling…" + _set_transaction_feedback("Selling…") return if ( _network_sale_service == null or buyer_id.is_empty() or not _network_sale_service.can_request_sale(buyer_id) ): - _transaction_feedback.text = ( + _set_transaction_feedback( "Selling is not supported by this server." ) return - var selected_ids: Array[StringName] = _fish_selection.get_selected_ids() if ( _inventory == null or _sale_service == null or active_buyer == null - or selected_ids.is_empty() + or catch_ids.is_empty() ): return var preview: FishSaleResultType = _sale_service.preview_batch( - selected_ids, + catch_ids, active_buyer ) if not preview.is_success(): - _transaction_feedback.text = ( + _set_transaction_feedback( "favorited fish cannot be sold. " + "remove them from the selection first." if preview.status == FishSaleResultType.Status.FAVORITED @@ -3133,21 +4121,21 @@ func _on_sell_pressed() -> void: ) _refresh_inventory() return - _confirmation_catch_ids = selected_ids.duplicate() + _confirmation_catch_ids = catch_ids.duplicate() _confirmation_buyer = active_buyer _confirmation_buyer_id = active_buyer.id _confirmation_generation = _menu_generation var fish_label: String = "fish" _confirmation_message.text = ( - "sell %d %s to the %s for $%d?\ncombined base value: $%d" - % [ - preview.fish_count, - fish_label, - _get_buyer_display_group(active_buyer), - preview.payout, - preview.base_value, - ] - ) + "[center]sell %d %s to the %s for %s?\n" + + "combined base value: %s[/center]" + ) % [ + preview.fish_count, + fish_label, + _get_buyer_display_group(active_buyer), + CurrencyPresentationType.bbcode_amount(preview.payout, 22), + CurrencyPresentationType.bbcode_amount(preview.base_value, 22), + ] _sale_confirmation.visible = true if _shop_cooler_context_active: shop_cooler_modal_changed.emit(true) @@ -3193,7 +4181,7 @@ func _on_confirm_sale_pressed() -> void: and transaction_generation == _menu_generation and (visible or _shop_cooler_context_active) ): - _transaction_feedback.text = "Selling…" + _set_transaction_feedback("Selling…") func _can_use_shared_world_actions() -> bool: @@ -3206,7 +4194,7 @@ func _can_use_shared_world_actions() -> bool: func _on_network_sale_pending(_request_id: String) -> void: _sale_in_progress = true if visible or _shop_cooler_context_active: - _transaction_feedback.text = "Selling…" + _set_transaction_feedback("Selling…") _update_sale_summary() @@ -3224,11 +4212,12 @@ func _on_network_sale_finished( return _refresh_all() var feedback_message: String = ( - "Sale complete. $%d received." % payout + "Sale complete • %s received." + % CurrencyPresentationType.bbcode_amount(payout, 20) if accepted else message ) - _transaction_feedback.text = feedback_message + _set_transaction_feedback(feedback_message) if not accepted: _sale_unavailable.text = feedback_message _sale_unavailable.visible = true @@ -3284,7 +4273,7 @@ func _revalidate_confirmation() -> void: or _confirmation_buyer.id != _confirmation_buyer_id ): _close_sale_confirmation() - _transaction_feedback.text = "sale selection is no longer available." + _set_transaction_feedback("sale selection is no longer available.") return var preview: FishSaleResultType = _sale_service.preview_batch( _confirmation_catch_ids, @@ -3299,7 +4288,7 @@ func _revalidate_confirmation() -> void: ) ): _close_sale_confirmation() - _transaction_feedback.text = ( + _set_transaction_feedback( "favorited fish cannot be sold. " + "remove them from the selection first." if preview.status == FishSaleResultType.Status.FAVORITED @@ -3319,6 +4308,10 @@ func _get_buyer_display_group( return "buyer" +func _set_transaction_feedback(message: String) -> void: + _transaction_feedback.text = "[center]%s[/center]" % message + + func _get_active_sale_buyer() -> FishBuyerProfileType: if _sale_buyer_override != null and _sale_buyer_override.is_valid(): return _sale_buyer_override @@ -3447,7 +4440,9 @@ func _release_focus_from(node: Node, fallback: Control) -> void: ) ): return - get_viewport().gui_release_focus() + var current_viewport: Viewport = get_viewport() + if current_viewport != null: + current_viewport.gui_release_focus() if ( visible and not _transitioning diff --git a/ui/player_menu.tscn b/ui/player_menu.tscn index aac0884..e20077c 100644 --- a/ui/player_menu.tscn +++ b/ui/player_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=25 format=3] +[gd_scene load_steps=34 format=3] [ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] @@ -23,6 +23,15 @@ [ext_resource type="PackedScene" path="res://ui/logbook_page.tscn" id="21_logbook_page"] [ext_resource type="Script" path="res://ui/components/inventory_notepad.gd" id="22_inventory_notepad"] [ext_resource type="Script" path="res://ui/components/organizer_tab.gd" id="23_organizer_tab"] +[ext_resource type="PackedScene" path="res://ui/the_net_page.tscn" id="24_the_net_page"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/inventory_simple.png" id="25_inventory_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/logbook_icon_simple.png" id="26_logbook_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/fishnet_simple.png" id="27_fishnet_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/mail_simple.png" id="28_mail_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/profile_simple.png" id="29_profile_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/online_simple.png" id="30_online_simple"] +[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/close.png" id="31_close"] +[ext_resource type="PackedScene" path="res://ui/components/currency_amount.tscn" id="32_currency"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"] @@ -166,21 +175,6 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 1 -[node name="CoolerEmpty" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/CoolerOuterWall/CoolerWallMargin/CoolerInnerLiner/CoolerInnerMargin/CoolerScroll/CoolerHost"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -theme_override_colors/font_color = Color(0.91, 0.97, 0.98, 0.82) -theme_override_font_sizes/font_size = 24 -text = "the cooler is empty" -horizontal_alignment = 1 -vertical_alignment = 1 - [node name="DetailConstellation" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage"] unique_name_in_owner = true layout_mode = 0 @@ -231,7 +225,7 @@ theme_override_font_sizes/font_size = 19 text = "cooler notes" horizontal_alignment = 1 -[node name="NotepadWalletValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"] +[node name="NotepadWalletValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("32_currency")] unique_name_in_owner = true layout_mode = 0 offset_left = 18.0 @@ -239,9 +233,7 @@ offset_top = 61.0 offset_right = 138.0 offset_bottom = 84.0 mouse_filter = 2 -theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1) -theme_override_font_sizes/font_size = 14 -text = "$0" +icon_size = 14.0 [node name="NotepadCapacityValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"] unique_name_in_owner = true @@ -374,12 +366,11 @@ mouse_filter = 2 theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1) theme_override_font_sizes/font_size = 18 -[node name="CoolerOfferValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerDetailStats/CoolerOfferRow"] +[node name="CoolerOfferValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerDetailStats/CoolerOfferRow" instance=ExtResource("32_currency")] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 -theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1) -theme_override_font_sizes/font_size = 15 +icon_size = 15.0 [node name="CoolerSelectionSummary" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"] unique_name_in_owner = true @@ -400,7 +391,7 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 theme_override_colors/font_color = Color(0.2, 0.18, 0.15, 0.86) -theme_override_font_sizes/font_size = 18 +theme_override_font_sizes/font_size = 20 text = "no fish selected" horizontal_alignment = 1 vertical_alignment = 1 @@ -449,12 +440,11 @@ theme_override_colors/font_color = Color(0.2, 0.18, 0.15, 0.86) theme_override_font_sizes/font_size = 18 text = "combined offer" -[node name="CoolerCombinedOfferValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerSelectionSummary/CoolerCombinedOfferRow"] +[node name="CoolerCombinedOfferValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerSelectionSummary/CoolerCombinedOfferRow" instance=ExtResource("32_currency")] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 -theme_override_colors/font_color = Color(0.2, 0.18, 0.15, 0.86) -theme_override_font_sizes/font_size = 14 +icon_size = 14.0 [node name="FavoriteBubble" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("12_ink_action")] unique_name_in_owner = true @@ -477,6 +467,16 @@ offset_right = 256.0 offset_bottom = 464.0 text = "sell fish" +[node name="SellAllBubble" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("12_ink_action")] +unique_name_in_owner = true +custom_minimum_size = Vector2(122, 44) +layout_mode = 0 +offset_left = 78.0 +offset_top = 426.0 +offset_right = 200.0 +offset_bottom = 470.0 +text = "sell all fish" + [node name="BagPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"] unique_name_in_owner = true visible = false @@ -704,12 +704,23 @@ theme_override_constants/margin_top = 20 theme_override_constants/margin_right = 20 theme_override_constants/margin_bottom = 20 -[node name="TackleDetailText" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin"] +[node name="Layout" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="TackleDetailText" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin/Layout"] unique_name_in_owner = true layout_mode = 2 text = "Select bait or a lure for details." autowrap_mode = 2 -vertical_alignment = 1 + +[node name="TackleEquipButton" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin/Layout" instance=ExtResource("12_ink_action")] +unique_name_in_owner = true +layout_mode = 2 +custom_minimum_size = Vector2(150, 68) +size_flags_horizontal = 4 +text = "equip worms" +allow_persistent_mark = true [node name="LogbookPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"] unique_name_in_owner = true @@ -722,6 +733,10 @@ mouse_filter = 1 [node name="CatalogLogbook" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage" instance=ExtResource("21_logbook_page")] unique_name_in_owner = true +[node name="TheNetPage" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot" instance=ExtResource("24_the_net_page")] +unique_name_in_owner = true +visible = false + [node name="MailPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"] unique_name_in_owner = true layout_mode = 0 @@ -734,9 +749,8 @@ script = ExtResource("18_mail_page") unique_name_in_owner = true visible = false layout_mode = 0 -offset_top = 112.0 offset_right = 1280.0 -offset_bottom = 710.0 +offset_bottom = 720.0 [node name="PlayersPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"] visible = false @@ -948,24 +962,31 @@ unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 8 -[node name="WalletBalance" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header"] +[node name="WalletHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header"] +layout_mode = 2 +text = "wallet" +theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) + +[node name="WalletBalance" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header" instance=ExtResource("32_currency")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -text = "wallet: $0" -theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) +alignment = 0 [node name="Separator" type="HSeparator" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"] unique_name_in_owner = true layout_mode = 2 -[node name="TransactionFeedback" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"] +[node name="TransactionFeedback" type="RichTextLabel" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"] unique_name_in_owner = true +custom_minimum_size = Vector2(0, 30) layout_mode = 2 text = "" -horizontal_alignment = 1 -theme_override_colors/font_color = Color(1, 0.82, 0.4, 1) -theme_override_font_sizes/font_size = 21 +bbcode_enabled = true +fit_content = true +scroll_active = false +theme_override_colors/default_color = Color(1, 0.82, 0.4, 1) +theme_override_font_sizes/normal_font_size = 21 [node name="Content" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"] unique_name_in_owner = true @@ -1040,12 +1061,16 @@ layout_mode = 2 text = "0 / 12" theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1) -[node name="HeldValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] +[node name="HeldValueHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar"] +visible = false +layout_mode = 2 +text = "held fish base value" +theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) + +[node name="HeldValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar" instance=ExtResource("32_currency")] unique_name_in_owner = true visible = false layout_mode = 2 -text = "held fish base value: $0" -theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) [node name="InventoryBody" type="BoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection"] unique_name_in_owner = true @@ -1349,12 +1374,15 @@ layout_mode = 2 theme_override_constants/separation = 12 alignment = 1 -[node name="ConfirmationMessage" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"] +[node name="ConfirmationMessage" type="RichTextLabel" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"] unique_name_in_owner = true +custom_minimum_size = Vector2(0, 64) layout_mode = 2 -theme_override_font_sizes/font_size = 22 +bbcode_enabled = true +fit_content = true +scroll_active = false +theme_override_font_sizes/normal_font_size = 22 autowrap_mode = 2 -horizontal_alignment = 1 [node name="ConfirmationButtons" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"] layout_mode = 2 @@ -1390,15 +1418,17 @@ compact_reference_size = Vector2(840, 100) unique_name_in_owner = true layout_mode = 0 toggle_mode = true -text = "inventory" +icon = ExtResource("25_inventory_simple") +tooltip_text = "stuff" +accessibility_name = "stuff" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(112, 108) -desktop_anchor = Vector2(70, 60) -compact_anchor = Vector2(70, 73.3333) +desktop_anchor = Vector2(60, 60) +compact_anchor = Vector2(60, 73.3333) compact_minimum_size = Vector2(84, 82) -minimum_font_size = 20 -maximum_font_size = 22 +minimum_font_size = 25 +maximum_font_size = 25 horizontal_amplitude = 1.2 vertical_amplitude = 2.4 motion_period = 5.6 @@ -1410,12 +1440,14 @@ deformation_period = 6.4 unique_name_in_owner = true layout_mode = 0 toggle_mode = true -text = "logbook" +icon = ExtResource("26_logbook_simple") +tooltip_text = "logbook" +accessibility_name = "logbook" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(112, 108) -desktop_anchor = Vector2(210, 60) -compact_anchor = Vector2(210, 73.3333) +desktop_anchor = Vector2(180, 60) +compact_anchor = Vector2(180, 73.3333) compact_minimum_size = Vector2(84, 82) minimum_font_size = 25 maximum_font_size = 25 @@ -1426,16 +1458,40 @@ motion_phase = 2.85 deformation_amplitude = 0.012 deformation_period = 6.7 +[node name="TheNetTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"] +unique_name_in_owner = true +layout_mode = 0 +toggle_mode = true +icon = ExtResource("27_fishnet_simple") +tooltip_text = "fishnet" +accessibility_name = "net" +script = ExtResource("6_bubble") +profile = ExtResource("4_profile") +neutral_size = Vector2(112, 108) +desktop_anchor = Vector2(300, 60) +compact_anchor = Vector2(300, 73.3333) +compact_minimum_size = Vector2(84, 82) +minimum_font_size = 25 +maximum_font_size = 25 +horizontal_amplitude = 1.2 +vertical_amplitude = 2.3 +motion_period = 5.75 +motion_phase = 3.2 +deformation_amplitude = 0.011 +deformation_period = 6.45 + [node name="MailTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"] unique_name_in_owner = true layout_mode = 0 toggle_mode = true -text = "mail" +icon = ExtResource("28_mail_simple") +tooltip_text = "mail" +accessibility_name = "mail" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(112, 108) -desktop_anchor = Vector2(350, 60) -compact_anchor = Vector2(350, 73.3333) +desktop_anchor = Vector2(420, 60) +compact_anchor = Vector2(420, 73.3333) compact_minimum_size = Vector2(84, 82) minimum_font_size = 25 maximum_font_size = 25 @@ -1466,12 +1522,14 @@ vertical_alignment = 1 unique_name_in_owner = true layout_mode = 0 toggle_mode = true -text = "profile" +icon = ExtResource("29_profile_simple") +tooltip_text = "profile" +accessibility_name = "profile" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(112, 108) -desktop_anchor = Vector2(490, 60) -compact_anchor = Vector2(490, 73.3333) +desktop_anchor = Vector2(540, 60) +compact_anchor = Vector2(540, 73.3333) compact_minimum_size = Vector2(84, 82) minimum_font_size = 23 maximum_font_size = 25 @@ -1486,12 +1544,15 @@ deformation_period = 6.5 unique_name_in_owner = true layout_mode = 0 toggle_mode = true -text = "players" +texture_filter = 1 +icon = ExtResource("30_online_simple") +tooltip_text = "online" +accessibility_name = "online" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(112, 108) -desktop_anchor = Vector2(630, 60) -compact_anchor = Vector2(630, 73.3333) +desktop_anchor = Vector2(660, 60) +compact_anchor = Vector2(660, 73.3333) compact_minimum_size = Vector2(84, 82) minimum_font_size = 22 maximum_font_size = 24 @@ -1505,12 +1566,14 @@ deformation_period = 6.4 [node name="CloseButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"] unique_name_in_owner = true layout_mode = 0 -text = "close" +icon = ExtResource("31_close") +tooltip_text = "close" +accessibility_name = "close" script = ExtResource("6_bubble") profile = ExtResource("4_profile") neutral_size = Vector2(96, 94) -desktop_anchor = Vector2(770, 57) -compact_anchor = Vector2(770, 73.3333) +desktop_anchor = Vector2(780, 57) +compact_anchor = Vector2(780, 73.3333) compact_minimum_size = Vector2(76, 74) minimum_font_size = 23 maximum_font_size = 23 diff --git a/ui/players_page.gd b/ui/players_page.gd index d834176..380299e 100644 --- a/ui/players_page.gd +++ b/ui/players_page.gd @@ -1,11 +1,21 @@ class_name PlayersPage extends Control +const TOGGLE_STATE_COLOR := Color("c3dfe6") + var _service: NetworkPlayerListService -var _header: Label +var _discovery: DiscoveryClient +var _count_label: Label var _tabs: HBoxContainer var _list: VBoxContainer var _status: Label +var _host_settings_panel: PanelContainer +var _room_name_edit: LineEdit +var _online_toggle: Button +var _online_state_label: Label +var _discoverable_toggle: Button +var _discoverable_state_label: Label +var _host_discovery_status: Label var _current_tab := 0 @@ -15,13 +25,22 @@ func _ready() -> void: _build() -func setup(service: NetworkPlayerListService) -> void: +func setup( + service: NetworkPlayerListService, + discovery: DiscoveryClient, +) -> void: _service = service + _discovery = discovery _service.entries_changed.connect(_refresh) _service.moderation_finished.connect(func(_ok: bool, message: String) -> void: _status.text = message _refresh() ) + if _discovery != null: + _discovery.host_settings_changed.connect( + _on_host_settings_changed + ) + _discovery.host_status_changed.connect(_on_host_status_changed) _refresh() @@ -36,23 +55,17 @@ func deactivate() -> void: func _build() -> void: - var paper := PanelContainer.new() - paper.position = Vector2(58, 128) - paper.size = Vector2(1164, 538) - add_child(paper) - paper.add_theme_stylebox_override("panel", UtilityPageStyle.panel_style()) + var margin: MarginContainer = UtilityPageStyle.build_laptop_screen(self) var root := VBoxContainer.new() - root.add_theme_constant_override("separation", 10) - paper.add_child(root) - _header = Label.new() - _header.add_theme_font_size_override("font_size", 27) - _header.add_theme_color_override( - "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY - ) - root.add_child(_header) + root.add_theme_constant_override("separation", 9) + margin.add_child(root) + var tab_row := HBoxContainer.new() + tab_row.add_theme_constant_override("separation", 16) + root.add_child(tab_row) _tabs = HBoxContainer.new() + _tabs.size_flags_horizontal = Control.SIZE_EXPAND_FILL _tabs.add_theme_constant_override("separation", 10) - root.add_child(_tabs) + tab_row.add_child(_tabs) for index: int in 3: var button := Button.new() button.text = ["players", "relationships", "banned"][index] @@ -60,12 +73,21 @@ func _build() -> void: button.pressed.connect(_select_tab.bind(index)) UtilityPageStyle.apply_ocean_button(button) _tabs.add_child(button) + _count_label = Label.new() + _count_label.add_theme_font_size_override("font_size", 17) + _count_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _count_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + tab_row.add_child(_count_label) + _build_host_settings(root) var scroll := ScrollContainer.new() - scroll.custom_minimum_size = Vector2(0, 392) + scroll.custom_minimum_size = Vector2(0, 310) + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED root.add_child(scroll) _list = VBoxContainer.new() - _list.custom_minimum_size = Vector2(1080, 0) + _list.custom_minimum_size = Vector2(1032, 0) _list.add_theme_constant_override("separation", 7) scroll.add_child(_list) _status = Label.new() @@ -75,8 +97,104 @@ func _build() -> void: root.add_child(_status) +func _build_host_settings(root: VBoxContainer) -> void: + _host_settings_panel = PanelContainer.new() + _host_settings_panel.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + root.add_child(_host_settings_panel) + var row := HBoxContainer.new() + row.custom_minimum_size.y = 58.0 + row.add_theme_constant_override("separation", 10) + _host_settings_panel.add_child(row) + var label := Label.new() + label.text = "room name" + label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + row.add_child(label) + _room_name_edit = LineEdit.new() + _room_name_edit.custom_minimum_size = Vector2(300.0, 42.0) + _room_name_edit.max_length = DiscoveryClient.MAX_ROOM_NAME_LENGTH + _room_name_edit.placeholder_text = "Player Name's Server" + UtilityPageStyle.apply_ocean_line_edit(_room_name_edit) + _room_name_edit.text_submitted.connect( + func(_value: String) -> void: _commit_room_name() + ) + _room_name_edit.focus_exited.connect(_commit_room_name) + row.add_child(_room_name_edit) + _online_toggle = _build_host_toggle("online", row) + _online_state_label = _online_toggle.get_node("StateBadge/State") as Label + _online_toggle.pressed.connect(_on_online_pressed) + _discoverable_toggle = _build_host_toggle("discovery", row) + _discoverable_state_label = ( + _discoverable_toggle.get_node("StateBadge/State") as Label + ) + _discoverable_toggle.pressed.connect(_on_discoverable_pressed) + _host_discovery_status = Label.new() + _host_discovery_status.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _host_discovery_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + _host_discovery_status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _host_discovery_status.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _host_discovery_status.add_theme_font_size_override("font_size", 14) + _host_discovery_status.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + row.add_child(_host_discovery_status) + + +func _build_host_toggle(label_text: String, row: HBoxContainer) -> Button: + var button := Button.new() + button.custom_minimum_size = Vector2(150.0, 46.0) + button.text = label_text + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.clip_contents = false + UtilityPageStyle.apply_ocean_button(button) + row.add_child(button) + var badge := PanelContainer.new() + badge.name = "StateBadge" + badge.mouse_filter = Control.MOUSE_FILTER_IGNORE + badge.z_index = 2 + button.add_child(badge) + badge.set_anchors_preset(Control.PRESET_CENTER_BOTTOM) + badge.offset_left = -39.0 + badge.offset_top = -12.0 + badge.offset_right = 39.0 + badge.offset_bottom = 12.0 + var state_label := Label.new() + state_label.name = "State" + state_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + state_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + state_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + state_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) + state_label.add_theme_font_size_override("font_size", 15) + state_label.add_theme_color_override("font_color", TOGGLE_STATE_COLOR) + badge.add_child(state_label) + return button + + +func _set_host_toggle_state( + button: Button, + state_label: Label, + state_text: String, + enabled: bool, +) -> void: + state_label.text = state_text + var badge := state_label.get_parent() as PanelContainer + badge.add_theme_stylebox_override( + "panel", + UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_SELECTED + if enabled + else UtilityPageStyle.OCEAN_FIELD, + 12, + ), + ) + button.queue_redraw() + + func _select_tab(index: int) -> void: - if index == 2 and (_service == null or not _service.is_local_host()): + if index == 2 and (_service == null or not _service.is_local_moderator()): return _current_tab = index _refresh() @@ -86,13 +204,16 @@ func _select_tab(index: int) -> void: func _refresh() -> void: if _service == null or _list == null: return - _header.text = "Players\n%d / %d connected" % [ + _count_label.text = "%d / %d connected" % [ _service.get_connected_count(), _service.get_max_players(), ] + if _current_tab == 2 and not _service.is_local_moderator(): + _current_tab = 0 for index: int in _tabs.get_child_count(): var button := _tabs.get_child(index) as Button button.button_pressed = index == _current_tab - button.visible = index != 2 or _service.is_local_host() + button.visible = index != 2 or _service.is_local_moderator() + _refresh_host_settings() for child: Node in _list.get_children(): child.queue_free() match _current_tab: @@ -104,8 +225,114 @@ func _refresh() -> void: _build_ban_rows() +func _refresh_host_settings() -> void: + if _host_settings_panel == null: + return + var host_visible: bool = _service.is_local_host() and _current_tab == 0 + _host_settings_panel.visible = host_visible + if not host_visible or _discovery == null: + return + if not _room_name_edit.has_focus(): + _room_name_edit.text = _discovery.get_room_name() + var is_open: bool = _service.is_open_host() + var is_discoverable: bool = _discovery.is_discoverable() + _set_host_toggle_state( + _online_toggle, + _online_state_label, + "OPEN" if is_open else "CLOSED", + is_open, + ) + _set_host_toggle_state( + _discoverable_toggle, + _discoverable_state_label, + "ON" if is_discoverable else "OFF", + is_discoverable, + ) + _online_toggle.disabled = not _service.is_local_host() + _online_toggle.tooltip_text = ( + "Close this game to new connections." + if is_open + else "Open this game so other players can connect." + ) + var can_publish: bool = ( + _service.is_local_host() + and _discovery.is_configured() + and is_open + ) + _discoverable_toggle.disabled = not can_publish + _discoverable_toggle.tooltip_text = ( + "Stop advertising this game in the public room browser." + if is_discoverable + else "Advertise this open game in the public room browser." + if can_publish + else "Open the game before enabling discovery." + if _discovery.is_configured() + else "Room discovery is not configured in this build." + ) + _on_host_status_changed( + _discovery.get_host_status_message(), + _discovery.host_status_is_error(), + ) + + +func _commit_room_name() -> void: + if _discovery == null: + return + if not _discovery.set_room_name(_room_name_edit.text): + _room_name_edit.text = _discovery.get_room_name() + else: + _room_name_edit.text = _discovery.get_room_name() + + +func _on_online_pressed() -> void: + if _service == null or not _service.is_local_host(): + return + _service.set_host_open(not _service.is_open_host()) + _refresh_host_settings() + + +func _on_discoverable_pressed() -> void: + if _discovery == null: + return + _discovery.set_discoverable(not _discovery.is_discoverable()) + _refresh_host_settings() + + +func _on_host_settings_changed( + room_name: String, + discoverable: bool, +) -> void: + if _room_name_edit != null and not _room_name_edit.has_focus(): + _room_name_edit.text = room_name + if _discoverable_state_label != null: + _set_host_toggle_state( + _discoverable_toggle, + _discoverable_state_label, + "ON" if discoverable else "OFF", + discoverable, + ) + _refresh_host_settings() + + +func _on_host_status_changed(message: String, is_error: bool) -> void: + if _host_discovery_status == null: + return + var tooltip_only: bool = message in [ + "Open the game before listing it publicly.", + "Open the game before enabling discovery.", + ] + _host_discovery_status.text = "" if tooltip_only else message + _host_discovery_status.visible = not _host_discovery_status.text.is_empty() + _host_discovery_status.add_theme_color_override( + "font_color", + UtilityPageStyle.OCEAN_DANGER + if is_error + else UtilityPageStyle.OCEAN_TEXT_SECONDARY, + ) + + func _build_active_rows() -> void: - if _service.is_local_host(): + if _service.is_local_moderator(): _build_session_artwork_controls() var entries := _service.get_entries() if entries.is_empty(): @@ -114,10 +341,12 @@ func _build_active_rows() -> void: for entry: PlayerListEntry in entries: var row := _make_row() var identity := Label.new() - identity.custom_minimum_size.x = 515 + identity.custom_minimum_size.x = 430 var markers: Array[String] = [] if entry.is_host: markers.append("host") + if entry.is_operator: + markers.append("operator") if entry.is_local_player: markers.append("You") identity.text = "%s%s · %s\n%s" % [ @@ -126,7 +355,7 @@ func _build_active_rows() -> void: entry.compact_fingerprint, entry.continuity_state, ] - identity.tooltip_text = NetworkIdentityCrypto.format_fingerprint( + identity.tooltip_text = "Full identity fingerprint:\n%s" % ( entry.full_fingerprint ) identity.add_theme_color_override( @@ -156,6 +385,12 @@ func _build_active_rows() -> void: block.pressed.connect(_confirm_block.bind(entry)) UtilityPageStyle.apply_ocean_button(block) row.add_child(block) + if entry.can_manage_operator: + var operator := Button.new() + operator.text = "deop" if entry.is_operator else "op" + operator.pressed.connect(_confirm_operator.bind(entry)) + UtilityPageStyle.apply_ocean_button(operator) + row.add_child(operator) var kick := Button.new() kick.text = "kick" kick.disabled = not entry.can_kick @@ -168,7 +403,6 @@ func _build_active_rows() -> void: ban.pressed.connect(_confirm_ban.bind(entry)) UtilityPageStyle.apply_ocean_button(ban) row.add_child(ban) - _list.add_child(row) func _build_session_artwork_controls() -> void: @@ -195,7 +429,6 @@ func _build_session_artwork_controls() -> void: ) UtilityPageStyle.apply_ocean_button(reset) row.add_child(reset) - _list.add_child(row) func _build_relationship_rows() -> void: @@ -234,7 +467,6 @@ func _build_relationship_rows() -> void: ) UtilityPageStyle.apply_ocean_button(unmute) row.add_child(unmute) - _list.add_child(row) func _build_ban_rows() -> void: @@ -262,14 +494,18 @@ func _build_ban_rows() -> void: unban.pressed.connect(_confirm_unban.bind(fingerprint)) UtilityPageStyle.apply_ocean_button(unban) row.add_child(unban) - _list.add_child(row) func _make_row() -> HBoxContainer: + var panel := PanelContainer.new() + panel.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + _list.add_child(panel) var row := HBoxContainer.new() row.custom_minimum_size.y = 58 row.add_theme_constant_override("separation", 8) - row.add_theme_constant_override("outline_size", 1) + panel.add_child(row) return row @@ -313,6 +549,24 @@ func _confirm_ban(entry: PlayerListEntry) -> void: ) +func _confirm_operator(entry: PlayerListEntry) -> void: + var enabled: bool = not entry.is_operator + _confirm( + ( + "Grant operator access to %s for this room session?" + if enabled + else "Remove operator access from %s?" + ) % entry.display_name, + func() -> void: + _service.set_operator( + entry.peer_id, + entry.full_fingerprint, + enabled, + entry.revision, + ) + ) + + func _confirm_unban(fingerprint: String) -> void: _confirm("Unban %s?" % NetworkIdentityCrypto.compact_suffix(fingerprint), func() -> void: _service.unban(fingerprint) @@ -333,7 +587,32 @@ func _confirm(text: String, action: Callable) -> void: func _focus_first() -> void: - for child: Node in _tabs.get_children(): - if child is Button and child.visible and not child.disabled: - child.grab_focus() - return + var candidates: Array[Control] = [] + _collect_focusable_player_controls(self, candidates) + if candidates.is_empty(): + return + candidates.sort_custom(func(first: Control, second: Control) -> bool: + if not is_equal_approx(first.global_position.y, second.global_position.y): + return first.global_position.y < second.global_position.y + return first.global_position.x < second.global_position.x + ) + candidates[0].grab_focus() + + +func _collect_focusable_player_controls( + root: Node, + output: Array[Control], +) -> void: + for child: Node in root.get_children(): + if child == _tabs: + continue + var control := child as Control + if control != null and not control.is_visible_in_tree(): + continue + if ( + control != null + and control.focus_mode != Control.FOCUS_NONE + and not control is ScrollBar + ): + output.append(control) + _collect_focusable_player_controls(child, output) diff --git a/ui/profile_page.gd b/ui/profile_page.gd index eb434bc..3fc9fb4 100644 --- a/ui/profile_page.gd +++ b/ui/profile_page.gd @@ -2,12 +2,32 @@ class_name ProfilePage extends Control const CHECK_DEBOUNCE_SECONDS: float = 0.4 +const OPTION_GRID_COLUMNS: int = 6 +const FUR_PALETTE_GRID_COLUMNS: int = 10 +const FUR_CHANNEL_ICON_SIZE: int = 20 +const FEATURE_DRAWER_ANIMATION_SECONDS: float = 0.16 +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd") +const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd") +const VoiceProfilesType = preload( + "res://player/animalese_voice_profiles.gd" +) +const VOICE_CATEGORY_ID: String = "voice" var _service: NetworkProfileService +var _experience: PlayerExperience var _draft_name: String = "" var _draft_appearance: Dictionary = {} var _persisted_name: String = "" var _persisted_appearance: Dictionary = {} +var _draft_voice_id: String = VoiceProfilesType.DEFAULT_ID +var _persisted_voice_id: String = VoiceProfilesType.DEFAULT_ID +var _draft_speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID +var _persisted_speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID +var _draft_call_id: String = VoiceProfilesType.DEFAULT_CALL_ID +var _persisted_call_id: String = VoiceProfilesType.DEFAULT_CALL_ID var _category_id: String = "species" var _dirty: bool = false var _allow_duplicate: bool = false @@ -23,8 +43,20 @@ var _revert_button: Button var _discard_confirmation: PanelContainer var _confirmation_label: Label var _confirmation_confirm: Button +var _keep_editing_button: Button var _confirmation_action: String = "" var _debounce: Timer +var _experience_level: Label +var _experience_progress: ProgressBar +var _experience_value: Label +var _feature_preview_cache: Dictionary = {} +var _fur_swatch_icon_cache: Dictionary = {} +var _expanded_feature_drawers: Dictionary = {} +var _feature_drawer_animation_key: String = "" +var _scale_value_label: Label +var _voice_preview: AnimaleseVoiceType +var _voice_preview_tween: Tween +var _active_fur_color_id: String = "fur_pattern" func _ready() -> void: @@ -37,12 +69,27 @@ func _ready() -> void: add_child(_debounce) -func setup(service: NetworkProfileService) -> void: +func setup( + service: NetworkProfileService, + experience: PlayerExperience, + world_environment: WorldEnvironment, + world_sun: DirectionalLight3D, +) -> void: _service = service + _experience = experience if not _service.conflict_result.is_connected(_on_conflict_result): _service.conflict_result.connect(_on_conflict_result) _service.apply_finished.connect(_on_apply_finished) + if ( + _experience != null + and not _experience.experience_changed.is_connected( + _on_experience_changed + ) + ): + _experience.experience_changed.connect(_on_experience_changed) _load_persisted() + _refresh_experience() + _preview.setup_world_lighting(world_environment, world_sun) var identity_value := find_child("IdentityFingerprint", true, false) as Label if identity_value != null: var fingerprint := _service.get_identity_fingerprint() @@ -54,17 +101,32 @@ func setup(service: NetworkProfileService) -> void: ) +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + if _preview != null: + _preview.setup_controller_mapping(mapping_manager) + + +func set_world_pixel_size(pixel_size: int) -> void: + if _preview != null: + _preview.set_world_pixel_size(pixel_size) + + func activate() -> void: visible = true UtilityPageStyle.animate_in(self) if _service != null: _load_persisted() - _name_edit.grab_focus() + # Keep controller page switches on the Profile navigation bubble. Focusing + # the name field here summons the Android keyboard during LB/RB traversal. func deactivate() -> void: visible = false _debounce.stop() + if _voice_preview_tween != null and _voice_preview_tween.is_valid(): + _voice_preview_tween.kill() _preview.reset_view() @@ -108,134 +170,203 @@ func has_modal_confirmation() -> bool: func _build_ui() -> void: - var paper := PanelContainer.new() - paper.set_anchors_preset(Control.PRESET_FULL_RECT) - paper.offset_left = 64.0 - paper.offset_top = 28.0 - paper.offset_right = -64.0 - paper.offset_bottom = -28.0 - paper.add_theme_stylebox_override( - "panel", UtilityPageStyle.panel_style() - ) - add_child(paper) UtilityPageStyle.apply_page(self) - - var margin := MarginContainer.new() - for side: String in ["left", "right", "top", "bottom"]: - margin.add_theme_constant_override("margin_%s" % side, 18) - paper.add_child(margin) + var margin: MarginContainer = UtilityPageStyle.build_laptop_screen(self) var layout := VBoxContainer.new() - layout.add_theme_constant_override("separation", 5) + layout.add_theme_constant_override("separation", 8) margin.add_child(layout) - var heading := Label.new() - heading.text = "Player Profile" - heading.add_theme_font_size_override("font_size", 25) - heading.add_theme_color_override( - "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY - ) - layout.add_child(heading) - - var name_row := HBoxContainer.new() - name_row.add_theme_constant_override("separation", 10) - layout.add_child(name_row) - var name_stack := VBoxContainer.new() - name_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL - name_row.add_child(name_stack) + var account_row := HBoxContainer.new() + account_row.add_theme_constant_override("separation", 14) + layout.add_child(account_row) + var account_stack := VBoxContainer.new() + account_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL + account_stack.add_theme_constant_override("separation", 2) + account_row.add_child(account_stack) + var name_line := HBoxContainer.new() + name_line.add_theme_constant_override("separation", 10) + account_stack.add_child(name_line) var name_label := Label.new() name_label.text = "player name" + name_label.custom_minimum_size.x = 98.0 + name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER name_label.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY ) - name_stack.add_child(name_label) + name_line.add_child(name_label) _name_edit = LineEdit.new() _name_edit.max_length = NetworkProtocol.MAX_DISPLAY_NAME_LENGTH _name_edit.placeholder_text = "Player" - _name_edit.custom_minimum_size = Vector2(300, 40) + _name_edit.custom_minimum_size = Vector2(280, 34) UtilityPageStyle.apply_ocean_line_edit(_name_edit) + _name_edit.add_theme_stylebox_override( + "normal", UtilityPageStyle.ocean_button_style( + UtilityPageStyle.OCEAN_PANEL_MID + ) + ) _name_edit.text_changed.connect(_on_name_changed) - name_stack.add_child(_name_edit) + name_line.add_child(_name_edit) var helper := Label.new() helper.text = "Shown to other players in multiplayer." + helper.add_theme_font_size_override("font_size", 12) helper.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY ) - name_stack.add_child(helper) - _apply_button = Button.new() - _apply_button.text = "apply" - _apply_button.custom_minimum_size = Vector2(92, 40) - UtilityPageStyle.apply_ocean_button(_apply_button) - _apply_button.pressed.connect(_apply) - name_row.add_child(_apply_button) - _revert_button = Button.new() - _revert_button.text = "revert" - _revert_button.custom_minimum_size = Vector2(88, 40) - UtilityPageStyle.apply_ocean_button(_revert_button) - _revert_button.pressed.connect(_revert) - name_row.add_child(_revert_button) - var defaults_button := Button.new() - defaults_button.text = "defaults" - defaults_button.custom_minimum_size = Vector2(88, 40) - UtilityPageStyle.apply_ocean_button(defaults_button) - defaults_button.pressed.connect(_show_confirmation.bind("defaults")) - name_row.add_child(defaults_button) - + account_stack.add_child(helper) _name_status = Label.new() + _name_status.add_theme_font_size_override("font_size", 12) _name_status.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY ) - layout.add_child(_name_status) + account_stack.add_child(_name_status) _suggestions = HBoxContainer.new() - _suggestions.add_theme_constant_override("separation", 8) - layout.add_child(_suggestions) + _suggestions.add_theme_constant_override("separation", 6) + account_stack.add_child(_suggestions) var identity_value := Label.new() identity_value.name = "IdentityFingerprint" - identity_value.text = "Identity • Stored on this device" + identity_value.text = "identity • stored on this device" identity_value.tooltip_text = ( "This identity helps other players recognize you between sessions." ) + identity_value.add_theme_font_size_override("font_size", 12) identity_value.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY ) - layout.add_child(identity_value) + account_stack.add_child(identity_value) - var body := HBoxContainer.new() - body.size_flags_vertical = Control.SIZE_EXPAND_FILL - body.add_theme_constant_override("separation", 12) - layout.add_child(body) - _category_list = VBoxContainer.new() - _category_list.custom_minimum_size = Vector2(140, 0) - body.add_child(_category_list) - _option_list = VBoxContainer.new() - _option_list.custom_minimum_size = Vector2(190, 0) - body.add_child(_option_list) - var preview_stack := VBoxContainer.new() - preview_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL - preview_stack.alignment = BoxContainer.ALIGNMENT_CENTER - body.add_child(preview_stack) - _preview = preload("res://ui/profile_preview.tscn").instantiate() - preview_stack.add_child(_preview) - var preview_actions := HBoxContainer.new() - preview_actions.alignment = BoxContainer.ALIGNMENT_CENTER - preview_actions.add_theme_constant_override("separation", 10) - preview_stack.add_child(preview_actions) - var preview_note := Label.new() - preview_note.text = "Current player • drag or use left / right" - preview_note.add_theme_color_override( + var experience_row := HBoxContainer.new() + experience_row.add_theme_constant_override("separation", 9) + account_stack.add_child(experience_row) + _experience_level = Label.new() + _experience_level.custom_minimum_size.x = 64.0 + _experience_level.add_theme_font_size_override("font_size", 14) + _experience_level.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + experience_row.add_child(_experience_level) + _experience_progress = ProgressBar.new() + _experience_progress.custom_minimum_size = Vector2(210.0, 14.0) + _experience_progress.max_value = 1.0 + _experience_progress.show_percentage = false + _experience_progress.add_theme_stylebox_override( + "background", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_PANEL_MID, 7 + ) + ) + _experience_progress.add_theme_stylebox_override( + "fill", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_SELECTED, 7 + ) + ) + experience_row.add_child(_experience_progress) + _experience_value = Label.new() + _experience_value.add_theme_font_size_override("font_size", 14) + _experience_value.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY ) - preview_actions.add_child(preview_note) + experience_row.add_child(_experience_value) + + var actions := HBoxContainer.new() + actions.alignment = BoxContainer.ALIGNMENT_END + actions.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + actions.add_theme_constant_override("separation", 7) + account_row.add_child(actions) + _apply_button = Button.new() + _apply_button.text = "apply" + _apply_button.custom_minimum_size.x = 72.0 + UtilityPageStyle.apply_compact_ocean_button(_apply_button) + _apply_button.pressed.connect(_apply) + actions.add_child(_apply_button) + _revert_button = Button.new() + _revert_button.text = "revert" + _revert_button.custom_minimum_size.x = 76.0 + UtilityPageStyle.apply_compact_ocean_button(_revert_button) + _revert_button.pressed.connect(_revert) + actions.add_child(_revert_button) + var defaults_button := Button.new() + defaults_button.text = "defaults" + defaults_button.custom_minimum_size.x = 82.0 + UtilityPageStyle.apply_compact_ocean_button(defaults_button) + defaults_button.pressed.connect(_show_confirmation.bind("defaults")) + actions.add_child(defaults_button) + + var body_panel := PanelContainer.new() + body_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL + body_panel.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + layout.add_child(body_panel) + var body_margin := MarginContainer.new() + body_margin.add_theme_constant_override("margin_left", 14) + body_margin.add_theme_constant_override("margin_top", 10) + body_margin.add_theme_constant_override("margin_right", 14) + body_margin.add_theme_constant_override("margin_bottom", 10) + body_panel.add_child(body_margin) + var body := HBoxContainer.new() + body.alignment = BoxContainer.ALIGNMENT_BEGIN + body.add_theme_constant_override("separation", 16) + body_margin.add_child(body) + _category_list = VBoxContainer.new() + _category_list.custom_minimum_size = Vector2(120, 0) + _category_list.add_theme_constant_override("separation", 5) + body.add_child(_category_list) + _option_list = VBoxContainer.new() + _option_list.custom_minimum_size = Vector2(360, 0) + _option_list.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _option_list.size_flags_vertical = Control.SIZE_EXPAND_FILL + _option_list.add_theme_constant_override("separation", 5) + body.add_child(_option_list) + var body_spacer := Control.new() + body_spacer.custom_minimum_size.x = 12.0 + body_spacer.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + body.add_child(body_spacer) + var preview_stack := VBoxContainer.new() + preview_stack.custom_minimum_size.x = 260.0 + preview_stack.size_flags_vertical = Control.SIZE_EXPAND_FILL + preview_stack.alignment = BoxContainer.ALIGNMENT_CENTER + preview_stack.add_theme_constant_override("separation", 7) + body.add_child(preview_stack) + var preview_frame := PanelContainer.new() + preview_frame.custom_minimum_size = Vector2(260.0, 0.0) + preview_frame.size_flags_vertical = Control.SIZE_EXPAND_FILL + preview_frame.add_theme_stylebox_override( + "panel", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_FIELD, 18 + ) + ) + preview_stack.add_child(preview_frame) + var preview_layer := Control.new() + preview_layer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + preview_layer.size_flags_vertical = Control.SIZE_EXPAND_FILL + preview_layer.mouse_filter = Control.MOUSE_FILTER_PASS + preview_frame.add_child(preview_layer) + _preview = preload("res://ui/profile_preview.tscn").instantiate() + _preview.custom_minimum_size = Vector2(260.0, 0.0) + _preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _preview.size_flags_vertical = Control.SIZE_EXPAND_FILL + preview_layer.add_child(_preview) + _preview.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) var reset_view := Button.new() - reset_view.text = "reset view" + reset_view.text = "↶" + reset_view.tooltip_text = "reset view" + reset_view.custom_minimum_size = Vector2(36.0, 36.0) + reset_view.position = Vector2(-8.0, -8.0) + reset_view.z_index = 2 reset_view.pressed.connect(_preview.reset_view) - UtilityPageStyle.apply_ocean_button(reset_view) - preview_actions.add_child(reset_view) + UtilityPageStyle.apply_compact_ocean_button(reset_view) + reset_view.add_theme_font_size_override("font_size", 22) + preview_layer.add_child(reset_view) _discard_confirmation = PanelContainer.new() _discard_confirmation.visible = false _discard_confirmation.set_anchors_and_offsets_preset(Control.PRESET_CENTER) _discard_confirmation.custom_minimum_size = Vector2(430, 190) + _discard_confirmation.add_theme_stylebox_override( + "panel", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_PANEL_MID, 14 + ) + ) add_child(_discard_confirmation) var confirm_stack := VBoxContainer.new() confirm_stack.alignment = BoxContainer.ALIGNMENT_CENTER @@ -249,31 +380,38 @@ func _build_ui() -> void: confirm_stack.add_child(confirm_buttons) _confirmation_confirm = Button.new() _confirmation_confirm.pressed.connect(_confirm_pending_action) + UtilityPageStyle.apply_ocean_button(_confirmation_confirm) confirm_buttons.add_child(_confirmation_confirm) - var keep := Button.new() - keep.name = "KeepEditing" - keep.unique_name_in_owner = true - keep.text = "keep editing" - keep.pressed.connect(func() -> void: + _keep_editing_button = Button.new() + _keep_editing_button.text = "keep editing" + _keep_editing_button.pressed.connect(func() -> void: _discard_confirmation.visible = false _name_edit.grab_focus() ) - confirm_buttons.add_child(keep) + UtilityPageStyle.apply_ocean_button(_keep_editing_button) + confirm_buttons.add_child(_keep_editing_button) + _voice_preview = AnimaleseVoiceType.new() + _voice_preview.name = "ProfileVoicePreview" + add_child(_voice_preview) _build_categories() func _build_categories() -> void: for child: Node in _category_list.get_children(): child.queue_free() - for category_id: String in CharacterCustomizationCatalog.CATEGORY_IDS: + var category_ids: Array[String] = [] + for appearance_category: String in CharacterCustomizationCatalog.CATEGORY_IDS: + category_ids.append(appearance_category) + category_ids.append(VOICE_CATEGORY_ID) + for category_id: String in category_ids: var button := Button.new() - button.text = CharacterCustomizationCatalog.category_label(category_id) + button.text = _category_label(category_id) button.toggle_mode = true - button.custom_minimum_size = Vector2(0, 34) + button.custom_minimum_size.x = 120.0 button.button_pressed = category_id == _category_id button.pressed.connect(_select_category.bind(category_id)) - UtilityPageStyle.apply_ocean_button(button) + UtilityPageStyle.apply_compact_ocean_button(button) _category_list.add_child(button) _refresh_options() @@ -284,7 +422,7 @@ func _select_category(category_id: String) -> void: var button := child as Button if button != null: button.button_pressed = button.text == ( - CharacterCustomizationCatalog.category_label(category_id) + _category_label(category_id) ) _refresh_options() @@ -293,31 +431,640 @@ func _refresh_options() -> void: for child: Node in _option_list.get_children(): child.queue_free() var title := Label.new() - title.text = CharacterCustomizationCatalog.category_label(_category_id) - title.add_theme_font_size_override("font_size", 22) + title.text = _category_label(_category_id) + title.add_theme_font_size_override("font_size", 18) title.add_theme_color_override( "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY ) _option_list.add_child(title) - var options := CharacterCustomizationCatalog.options_for(_category_id) + if _category_id == VOICE_CATEGORY_ID: + _build_voice_options() + return + if _category_id == CharacterCustomizationCatalog.SCALE_CATEGORY_ID: + _build_scale_option() + return + var options: Array = CharacterCustomizationCatalog.options_for(_category_id) if options.is_empty(): var empty := Label.new() empty.text = "More options coming later." empty.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _option_list.add_child(empty) return + if _category_id == "fur_pattern": + _build_fur_color_options(options) + return + if _category_id in CharacterCustomizationCatalog.FEATURE_CATEGORIES: + _build_feature_preview_options(options) + return for option: Dictionary in options: var option_id := str(option["id"]) var button := Button.new() - button.text = str(option["label"]) + button.text = "×" if option_id == "none" else str(option["label"]) + button.tooltip_text = "none" if option_id == "none" else str( + option["label"] + ) button.toggle_mode = true - button.custom_minimum_size = Vector2(0, 34) + button.custom_minimum_size.x = 170.0 button.button_pressed = _draft_appearance.get(_category_id) == option_id button.pressed.connect(_select_option.bind(_category_id, option_id)) - UtilityPageStyle.apply_ocean_button(button) + UtilityPageStyle.apply_compact_ocean_button(button) + if option_id == "none": + button.add_theme_font_size_override("font_size", 24) _option_list.add_child(button) +func _category_label(category_id: String) -> String: + return ( + "voice" + if category_id == VOICE_CATEGORY_ID + else CharacterCustomizationCatalog.category_label(category_id) + ) + + +func _build_voice_options() -> void: + var description := Label.new() + description.text = "Choose how your character sounds in chat." + description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + description.size_flags_horizontal = Control.SIZE_EXPAND_FILL + description.add_theme_font_size_override("font_size", 12) + description.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _option_list.add_child(description) + var grid := GridContainer.new() + grid.columns = 3 + grid.add_theme_constant_override("h_separation", 6) + grid.add_theme_constant_override("v_separation", 4) + grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _option_list.add_child(grid) + for option: Dictionary in VoiceProfilesType.OPTIONS: + var option_id := str(option.get("id", "")) + var button := Button.new() + button.text = str(option.get("label", option_id)) + button.toggle_mode = true + button.custom_minimum_size = Vector2(108.0, 32.0) + button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + button.button_pressed = _draft_voice_id == option_id + button.pressed.connect(_select_voice_option.bind(option_id)) + UtilityPageStyle.apply_compact_ocean_button(button) + grid.add_child(button) + var divider := HSeparator.new() + divider.custom_minimum_size.y = 2.0 + _option_list.add_child(divider) + var speed_title := Label.new() + speed_title.text = "speech speed" + speed_title.add_theme_font_size_override("font_size", 13) + speed_title.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(speed_title) + var speed_description := Label.new() + speed_description.text = "Controls chat speech on this device only." + speed_description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + speed_description.size_flags_horizontal = Control.SIZE_EXPAND_FILL + speed_description.add_theme_font_size_override("font_size", 12) + speed_description.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _option_list.add_child(speed_description) + var speed_grid := GridContainer.new() + speed_grid.columns = 3 + speed_grid.add_theme_constant_override("h_separation", 6) + speed_grid.add_theme_constant_override("v_separation", 4) + speed_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _option_list.add_child(speed_grid) + for option: Dictionary in VoiceProfilesType.SPEED_OPTIONS: + var speed_id := str(option.get("id", "")) + var speed_button := Button.new() + speed_button.text = str(option.get("label", speed_id)) + speed_button.tooltip_text = "%d characters per second" % roundi( + float(option.get("characters_per_second", 28.0)) + ) + speed_button.toggle_mode = true + speed_button.custom_minimum_size = Vector2(108.0, 32.0) + speed_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + speed_button.button_pressed = _draft_speech_speed_id == speed_id + speed_button.pressed.connect( + _select_speech_speed_option.bind(speed_id) + ) + UtilityPageStyle.apply_compact_ocean_button(speed_button) + speed_grid.add_child(speed_button) + var call_divider := HSeparator.new() + call_divider.custom_minimum_size.y = 2.0 + _option_list.add_child(call_divider) + var call_title := Label.new() + call_title.text = "call" + call_title.add_theme_font_size_override("font_size", 13) + call_title.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(call_title) + var call_description := Label.new() + call_description.text = "Press G to make this sound." + call_description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + call_description.size_flags_horizontal = Control.SIZE_EXPAND_FILL + call_description.add_theme_font_size_override("font_size", 12) + call_description.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _option_list.add_child(call_description) + var call_grid := GridContainer.new() + call_grid.columns = 2 + call_grid.add_theme_constant_override("h_separation", 8) + call_grid.add_theme_constant_override("v_separation", 8) + _option_list.add_child(call_grid) + for option: Dictionary in VoiceProfilesType.CALL_OPTIONS: + var call_id := str(option.get("id", "")) + var call_button := Button.new() + call_button.text = str(option.get("label", call_id)) + call_button.toggle_mode = true + call_button.custom_minimum_size = Vector2(108.0, 32.0) + call_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + call_button.button_pressed = _draft_call_id == call_id + call_button.pressed.connect(_select_call_option.bind(call_id)) + UtilityPageStyle.apply_compact_ocean_button(call_button) + call_grid.add_child(call_button) + + +func _select_voice_option(voice_id: String) -> void: + if not VoiceProfilesType.is_valid(voice_id): + return + _draft_voice_id = voice_id + _dirty = _draft_differs() + _refresh_options() + _refresh_actions() + _play_voice_preview() + + +func _select_speech_speed_option(speed_id: String) -> void: + if not VoiceProfilesType.is_valid_speed(speed_id): + return + _draft_speech_speed_id = speed_id + _dirty = _draft_differs() + _refresh_options() + _refresh_actions() + _play_voice_preview() + + +func _select_call_option(call_id: String) -> void: + if not VoiceProfilesType.is_valid_call(call_id): + return + _draft_call_id = call_id + _dirty = _draft_differs() + _refresh_options() + _refresh_actions() + + +func _play_voice_preview() -> void: + if _voice_preview_tween != null and _voice_preview_tween.is_valid(): + _voice_preview_tween.kill() + _voice_preview_tween = _voice_preview.speak_text( + self, + "hello there!", + "profile-preview", + _draft_voice_id, + VoiceProfilesType.speed_for(_draft_speech_speed_id), + ) + + +func _build_scale_option() -> void: + var description := Label.new() + description.text = "Adjust your character's visual size." + description.add_theme_font_size_override("font_size", 14) + description.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _option_list.add_child(description) + + var value_row := HBoxContainer.new() + value_row.add_theme_constant_override("separation", 10) + value_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _option_list.add_child(value_row) + + var small_label := Label.new() + small_label.text = "small" + small_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + small_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + value_row.add_child(small_label) + + var slider := HSlider.new() + slider.name = "CharacterScaleSlider" + slider.min_value = CharacterCustomizationCatalog.MIN_CHARACTER_SCALE + slider.max_value = CharacterCustomizationCatalog.MAX_CHARACTER_SCALE + slider.step = CharacterCustomizationCatalog.CHARACTER_SCALE_STEP + slider.custom_minimum_size = Vector2(220.0, 40.0) + slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL + slider.tooltip_text = "character size" + slider.value = CharacterCustomizationCatalog.character_scale( + _draft_appearance.get( + CharacterCustomizationCatalog.SCALE_CATEGORY_ID, + CharacterCustomizationCatalog.DEFAULT_CHARACTER_SCALE, + ) + ) + slider.value_changed.connect(_select_scale) + value_row.add_child(slider) + + var large_label := Label.new() + large_label.text = "large" + large_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + large_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + value_row.add_child(large_label) + + _scale_value_label = Label.new() + _scale_value_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _scale_value_label.add_theme_font_size_override("font_size", 20) + _scale_value_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(_scale_value_label) + _update_scale_value_label(float(slider.value)) + + var gameplay_note := Label.new() + gameplay_note.text = "Movement and collision stay the same." + gameplay_note.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + gameplay_note.add_theme_font_size_override("font_size", 12) + gameplay_note.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _option_list.add_child(gameplay_note) + + +func _build_feature_preview_options(_options: Array) -> void: + var scroll := ScrollContainer.new() + scroll.custom_minimum_size = Vector2(190.0, 0.0) + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO + _option_list.add_child(scroll) + var grid := GridContainer.new() + grid.columns = OPTION_GRID_COLUMNS + grid.add_theme_constant_override("h_separation", 8) + grid.add_theme_constant_override("v_separation", 8) + grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL + scroll.add_child(grid) + var selected_id := CharacterCustomizationCatalog.canonical_option_id( + _category_id, str(_draft_appearance.get(_category_id, "")) + ) + var groups: Array = CharacterCustomizationCatalog.feature_option_groups( + _category_id + ) + var expanded_drawer_id := str( + _expanded_feature_drawers.get(_category_id, "") + ) + if expanded_drawer_id.is_empty(): + for group: Dictionary in groups: + var group_options: Array = group.get("options", []) as Array + if group_options.size() <= 1: + continue + var representative_id := str( + (group_options[0] as Dictionary).get("id", "") + ) + for option: Dictionary in group_options: + if ( + str(option.get("id", "")) == selected_id + and selected_id != representative_id + ): + expanded_drawer_id = str(group.get("id", "")) + _expanded_feature_drawers[_category_id] = ( + expanded_drawer_id + ) + break + if not expanded_drawer_id.is_empty(): + break + + var animation_key := _feature_drawer_animation_key + for group: Dictionary in groups: + var group_id := str(group.get("id", "")) + var group_options: Array = group.get("options", []) as Array + if group_options.is_empty(): + continue + var has_variants := group_options.size() > 1 + var is_expanded := has_variants and group_id == expanded_drawer_id + for option_index: int in range(group_options.size()): + if option_index > 0 and not is_expanded: + continue + var option: Dictionary = group_options[option_index] as Dictionary + var button := _build_feature_option_button( + option, + selected_id, + group_id, + option_index == 0, + has_variants, + is_expanded, + ) + grid.add_child(button) + if ( + option_index > 0 + and animation_key == "%s:%s" % [_category_id, group_id] + ): + _animate_feature_drawer_button(button) + _feature_drawer_animation_key = "" + + +func _build_feature_option_button( + option: Dictionary, + selected_id: String, + drawer_id: String, + is_representative: bool, + has_variants: bool, + is_expanded: bool, +) -> Button: + var option_id := str(option.get("id", "")) + var button := Button.new() + var is_none := option_id == "none" + button.text = "×" if is_none else "" + button.tooltip_text = "none" if is_none else str( + option.get("label", option_id) + ) + if is_representative and has_variants: + button.tooltip_text += ( + " (hide variants)" if is_expanded else " (show variants)" + ) + button.toggle_mode = true + button.button_pressed = selected_id == option_id + button.custom_minimum_size = Vector2(84.0, 64.0) + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.clip_contents = true + if is_representative and has_variants: + button.pressed.connect( + _select_feature_drawer.bind(_category_id, drawer_id, option_id) + ) + else: + button.pressed.connect(_select_option.bind(_category_id, option_id)) + UtilityPageStyle.apply_compact_ocean_button(button) + button.custom_minimum_size = Vector2(84.0, 64.0) + if is_none: + button.add_theme_font_size_override("font_size", 28) + else: + var preview := TextureRect.new() + preview.texture = _feature_preview_texture(_category_id, option_id) + preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + preview.mouse_filter = Control.MOUSE_FILTER_IGNORE + var preview_height := 50.0 + var preview_width := clampf( + preview_height * PlayerVisualPresenter.feature_uv_aspect(_category_id), + 32.0, + 74.0, + ) + preview.custom_minimum_size = Vector2(preview_width, preview_height) + preview.size = Vector2(preview_width, preview_height) + preview.position = Vector2((84.0 - preview_width) * 0.5, 7.0) + button.add_child(preview) + if is_representative and has_variants: + var indicator := Label.new() + indicator.text = "<" if is_expanded else ">" + indicator.position = Vector2(66.0, 1.0) + indicator.size = Vector2(14.0, 16.0) + indicator.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + indicator.mouse_filter = Control.MOUSE_FILTER_IGNORE + indicator.add_theme_font_size_override("font_size", 13) + indicator.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + button.add_child(indicator) + return button + + +func _select_feature_drawer( + category_id: String, + drawer_id: String, + option_id: String, +) -> void: + if str(_expanded_feature_drawers.get(category_id, "")) == drawer_id: + _expanded_feature_drawers.erase(category_id) + else: + _expanded_feature_drawers[category_id] = drawer_id + _feature_drawer_animation_key = "%s:%s" % [category_id, drawer_id] + _select_option(category_id, option_id) + + +func _animate_feature_drawer_button(button: Button) -> void: + button.scale = Vector2(0.05, 1.0) + var faded := button.modulate + faded.a = 0.0 + button.modulate = faded + var tween := button.create_tween() + tween.set_parallel(true) + tween.tween_property( + button, "scale", Vector2.ONE, FEATURE_DRAWER_ANIMATION_SECONDS + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + tween.tween_property( + button, "modulate:a", 1.0, FEATURE_DRAWER_ANIMATION_SECONDS + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) + + +func _feature_preview_texture( + category_id: String, + option_id: String, +) -> Texture2D: + var cache_key := "%s:%s" % [category_id, option_id] + if _feature_preview_cache.has(cache_key): + return _feature_preview_cache[cache_key] as Texture2D + var source := CharacterCustomizationCatalog.texture_for( + category_id, option_id + ) + if source == null: + return null + var image := source.get_image() + var used := image.get_used_rect() + if used.size.x <= 0 or used.size.y <= 0: + _feature_preview_cache[cache_key] = source + return source + var padding := 12 + used.position -= Vector2i(padding, padding) + used.size += Vector2i(padding * 2, padding * 2) + used = used.intersection(Rect2i(Vector2i.ZERO, image.get_size())) + var cropped := image.get_region(used) + var preview := ImageTexture.create_from_image(cropped) + _feature_preview_cache[cache_key] = preview + return preview + + +func _build_fur_color_options(options: Array) -> void: + var pattern_label := Label.new() + pattern_label.text = "pattern" + pattern_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(pattern_label) + var pattern_row := HBoxContainer.new() + pattern_row.add_theme_constant_override("separation", 8) + _option_list.add_child(pattern_row) + var selected_style_id := str(_draft_appearance.get( + CharacterCustomizationCatalog.FUR_STYLE_ID, + CharacterCustomizationCatalog.DEFAULT_FUR_STYLE, + )) + for pattern_option: Dictionary in CharacterCustomizationCatalog.options_for( + CharacterCustomizationCatalog.FUR_STYLE_ID + ): + var pattern_id := str(pattern_option.get("id", "")) + var pattern_button := Button.new() + pattern_button.text = str(pattern_option.get("label", pattern_id)) + pattern_button.toggle_mode = true + pattern_button.button_pressed = selected_style_id == pattern_id + pattern_button.custom_minimum_size = Vector2(118.0, 32.0) + pattern_button.pressed.connect(_select_option.bind( + CharacterCustomizationCatalog.FUR_STYLE_ID, + pattern_id, + )) + UtilityPageStyle.apply_compact_ocean_button(pattern_button) + pattern_row.add_child(pattern_button) + + var channel_label := Label.new() + channel_label.text = "color channel" + channel_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(channel_label) + var channel_grid := GridContainer.new() + channel_grid.columns = 2 + channel_grid.add_theme_constant_override("h_separation", 8) + channel_grid.add_theme_constant_override("v_separation", 8) + _option_list.add_child(channel_grid) + for color_category_id: String in CharacterCustomizationCatalog.FUR_COLOR_IDS: + var selected_color_id := str(_draft_appearance.get( + color_category_id, + "white", + )) + var selected_color := CharacterCustomizationCatalog.option_color( + color_category_id, + selected_color_id, + ) + var channel_button := Button.new() + channel_button.text = CharacterCustomizationCatalog.fur_color_label( + color_category_id + ) + channel_button.icon = _fur_swatch_icon(selected_color) + channel_button.icon_alignment = HORIZONTAL_ALIGNMENT_LEFT + channel_button.alignment = HORIZONTAL_ALIGNMENT_LEFT + channel_button.add_theme_constant_override( + "icon_max_width", FUR_CHANNEL_ICON_SIZE + ) + channel_button.add_theme_constant_override("h_separation", 8) + channel_button.tooltip_text = "Edit %s: %s" % [ + CharacterCustomizationCatalog.fur_color_label(color_category_id), + _fur_option_label(options, selected_color_id), + ] + channel_button.toggle_mode = true + channel_button.custom_minimum_size = Vector2(140.0, 34.0) + channel_button.button_pressed = ( + _active_fur_color_id == color_category_id + ) + channel_button.pressed.connect( + _select_fur_color_slot.bind(color_category_id) + ) + UtilityPageStyle.apply_compact_ocean_button(channel_button) + channel_grid.add_child(channel_button) + + _build_fur_palette(_active_fur_color_id, options) + + +func _build_fur_palette(category_id: String, options: Array) -> void: + var selected_id: String = CharacterCustomizationCatalog.canonical_option_id( + category_id, + str(_draft_appearance.get(category_id, "white")), + ) + var label := Label.new() + label.text = "%s palette" % CharacterCustomizationCatalog.fur_color_label( + category_id + ) + label.add_theme_font_size_override("font_size", 14) + label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + _option_list.add_child(label) + + var grid := GridContainer.new() + grid.columns = FUR_PALETTE_GRID_COLUMNS + grid.add_theme_constant_override("h_separation", 6) + grid.add_theme_constant_override("v_separation", 6) + _option_list.add_child(grid) + for option: Dictionary in options: + var option_id: String = str(option.get("id", "")) + var color: Color = CharacterCustomizationCatalog.option_color( + category_id, option_id + ) + var button := Button.new() + button.text = "" + button.tooltip_text = "%s: %s" % [ + CharacterCustomizationCatalog.fur_color_label(category_id), + str(option.get("label", option_id)), + ] + button.toggle_mode = true + button.custom_minimum_size = Vector2(30.0, 30.0) + button.button_pressed = selected_id == option_id + button.pressed.connect(_select_option.bind(category_id, option_id)) + _apply_fur_swatch_style(button, color, button.button_pressed) + grid.add_child(button) + + +func _fur_option_label(options: Array, option_id: String) -> String: + for option: Dictionary in options: + if str(option.get("id", "")) == option_id: + return str(option.get("label", option_id)) + return option_id + + +func _fur_swatch_icon(color: Color) -> Texture2D: + var cache_id := color.to_html(true) + if _fur_swatch_icon_cache.has(cache_id): + return _fur_swatch_icon_cache[cache_id] as Texture2D + var image := Image.create( + FUR_CHANNEL_ICON_SIZE, + FUR_CHANNEL_ICON_SIZE, + false, + Image.FORMAT_RGBA8, + ) + var center := Vector2.ONE * (float(FUR_CHANNEL_ICON_SIZE) * 0.5) + var radius := float(FUR_CHANNEL_ICON_SIZE) * 0.39 + for y: int in range(FUR_CHANNEL_ICON_SIZE): + for x: int in range(FUR_CHANNEL_ICON_SIZE): + var sample_position := Vector2(float(x) + 0.5, float(y) + 0.5) + var alpha := clampf( + radius + 0.8 - sample_position.distance_to(center), + 0.0, + 1.0, + ) + image.set_pixel( + x, + y, + Color(color.r, color.g, color.b, color.a * alpha), + ) + var texture := ImageTexture.create_from_image(image) + _fur_swatch_icon_cache[cache_id] = texture + return texture + + +func _select_fur_color_slot(category_id: String) -> void: + if category_id not in CharacterCustomizationCatalog.FUR_COLOR_IDS: + return + _active_fur_color_id = category_id + _refresh_options() + + +func _apply_fur_swatch_style( + button: Button, + color: Color, + selected: bool, +) -> void: + var normal: StyleBoxFlat = UtilityPageStyle.rounded_style(color, 999) + var hover: StyleBoxFlat = UtilityPageStyle.rounded_style( + color.lightened(0.12), 999 + ) + var selected_style: StyleBoxFlat = UtilityPageStyle.rounded_style(color, 999) + selected_style.border_color = UtilityPageStyle.OCEAN_TEXT_PRIMARY + selected_style.set_border_width_all(3 if selected else 2) + button.add_theme_stylebox_override("normal", normal) + button.add_theme_stylebox_override("hover", hover) + button.add_theme_stylebox_override("pressed", selected_style) + button.add_theme_stylebox_override("focus", selected_style) + + func _select_option(category_id: String, option_id: String) -> void: _draft_appearance[category_id] = option_id _preview.apply_appearance_profile(_draft_appearance) @@ -326,6 +1073,26 @@ func _select_option(category_id: String, option_id: String) -> void: _refresh_actions() +func _select_scale(value: float) -> void: + var resolved_scale: float = CharacterCustomizationCatalog.character_scale( + value + ) + _draft_appearance[CharacterCustomizationCatalog.SCALE_CATEGORY_ID] = ( + resolved_scale + ) + _update_scale_value_label(resolved_scale) + _preview.apply_appearance_profile(_draft_appearance) + _dirty = _draft_differs() + _refresh_actions() + + +func _update_scale_value_label(value: float) -> void: + if _scale_value_label != null: + _scale_value_label.text = "%d%%" % ( + CharacterCustomizationCatalog.character_scale_percent(value) + ) + + func _on_name_changed(value: String) -> void: _draft_name = value _allow_duplicate = false @@ -358,6 +1125,7 @@ func _on_conflict_result( var button := Button.new() button.text = suggestion button.pressed.connect(_use_suggestion.bind(suggestion)) + UtilityPageStyle.apply_compact_ocean_button(button) _suggestions.add_child(button) var anyway := Button.new() anyway.text = "use anyway" @@ -365,9 +1133,33 @@ func _on_conflict_result( _allow_duplicate = true _name_status.text = "Duplicate name allowed for this apply." ) + UtilityPageStyle.apply_compact_ocean_button(anyway) _suggestions.add_child(anyway) +func _on_experience_changed(_total_experience: int, _level: int) -> void: + _refresh_experience() + + +func _refresh_experience() -> void: + if _experience_level == null: + return + if _experience == null: + _experience_level.text = "level 1" + _experience_progress.value = 0.0 + _experience_value.text = "0 / 100 xp" + return + var level: int = _experience.get_level() + var current: int = _experience.get_experience_in_level() + var required: int = _experience.get_experience_for_next_level() + _experience_level.text = "level %d" % level + _experience_progress.value = _experience.get_level_progress() + _experience_value.text = "%d / %d xp" % [current, required] + _experience_value.tooltip_text = "%d total xp" % ( + _experience.get_total_experience() + ) + + func _use_suggestion(value: String) -> void: _name_edit.text = value _draft_name = value @@ -381,7 +1173,14 @@ func _apply() -> void: if _service == null: return _apply_button.disabled = true - _service.apply_profile(_draft_name, _draft_appearance, _allow_duplicate) + _service.apply_profile( + _draft_name, + _draft_appearance, + _allow_duplicate, + _draft_voice_id, + _draft_speech_speed_id, + _draft_call_id, + ) func _on_apply_finished(accepted: bool, message: String) -> void: @@ -396,8 +1195,19 @@ func _load_persisted() -> void: return _persisted_name = _service.get_persisted_name() _persisted_appearance = _service.get_persisted_appearance() + _persisted_voice_id = _service.get_persisted_voice_id() + _persisted_speech_speed_id = ( + _service.get_persisted_speech_speed_id() + ) + _persisted_call_id = _service.get_persisted_call_id() _draft_name = _persisted_name _draft_appearance = _persisted_appearance.duplicate(true) + _draft_voice_id = _persisted_voice_id + _draft_speech_speed_id = _persisted_speech_speed_id + _draft_call_id = _persisted_call_id + TypewriterRevealType.set_characters_per_second( + VoiceProfilesType.speed_for(_persisted_speech_speed_id) + ) _name_edit.text = _draft_name _preview.apply_appearance_profile(_draft_appearance) _dirty = false @@ -426,13 +1236,16 @@ func _show_confirmation(action: String) -> void: else: _confirmation_label.text = "Discard unsaved profile changes?" _confirmation_confirm.text = "discard changes" - _discard_confirmation.get_node("%KeepEditing").grab_focus() + _keep_editing_button.grab_focus() func _confirm_pending_action() -> void: _discard_confirmation.visible = false if _confirmation_action == "defaults": _draft_appearance = CharacterCustomizationCatalog.default_snapshot() + _draft_voice_id = VoiceProfilesType.DEFAULT_ID + _draft_speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID + _draft_call_id = VoiceProfilesType.DEFAULT_CALL_ID _preview.apply_appearance_profile(_draft_appearance) _dirty = _draft_differs() _refresh_options() @@ -446,6 +1259,9 @@ func _draft_differs() -> bool: return ( _draft_name.strip_edges() != _persisted_name or _draft_appearance != _persisted_appearance + or _draft_voice_id != _persisted_voice_id + or _draft_speech_speed_id != _persisted_speech_speed_id + or _draft_call_id != _persisted_call_id ) diff --git a/ui/profile_preview.gd b/ui/profile_preview.gd index c0bef1c..4239ea0 100644 --- a/ui/profile_preview.gd +++ b/ui/profile_preview.gd @@ -1,18 +1,72 @@ class_name ProfilePreview extends SubViewportContainer +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const UIReferencePresentationType = preload( + "res://ui/ui_reference_presentation.gd" +) +const FRONT_FACING_YAW: float = PI +const DEFAULT_CAMERA_DISTANCE: float = 1.9 +const DEFAULT_CAMERA_PITCH: float = 0.12 +const MIN_CAMERA_DISTANCE: float = 1.05 +const MAX_CAMERA_DISTANCE: float = 2.8 +const CAMERA_ZOOM_STEP: float = 0.14 +const CAMERA_TARGET_HEIGHT: float = 0.95 + @export_range(0.1, 2.0, 0.05) var drag_sensitivity: float = 0.012 @export_range(0.1, 4.0, 0.1) var keyboard_speed: float = 1.8 @onready var _preview_root: Node3D = %PreviewRoot +@onready var _preview_environment: WorldEnvironment = %WorldEnvironment +@onready var _preview_sun: DirectionalLight3D = %KeyLight +@onready var _preview_camera: Camera3D = $Viewport/Camera3D +@onready var _pixelation_material: ShaderMaterial = material as ShaderMaterial var _dragging: bool = false +var _camera_yaw: float = 0.0 +var _camera_pitch: float = DEFAULT_CAMERA_PITCH +var _camera_distance: float = DEFAULT_CAMERA_DISTANCE var _visuals: Node3D +var _controller_mapping_manager: ControllerMappingManagerType +var _source_environment: WorldEnvironment +var _source_sun: DirectionalLight3D +var _world_pixel_size: int = PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE + + +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + + +func setup_world_lighting( + world_environment: WorldEnvironment, + world_sun: DirectionalLight3D, +) -> void: + _source_environment = world_environment + _source_sun = world_sun + _sync_world_lighting() + + +func set_world_pixel_size(pixel_size: int) -> void: + _world_pixel_size = clampi( + pixel_size, + PlayerSettings.MIN_WORLD_PIXEL_SIZE, + PlayerSettings.MAX_WORLD_PIXEL_SIZE, + ) + _refresh_pixelation_filter() func _ready() -> void: focus_mode = Control.FOCUS_ALL + texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST gui_input.connect(_on_gui_input) + resized.connect(_refresh_pixelation_filter) + get_window().size_changed.connect(_refresh_pixelation_filter) + _configure_preview_lighting() + reset_view() _visuals = PlayerVisualPresenter.instantiate_visuals() _preview_root.add_child(_visuals) var rod := _visuals.find_child("FishingRod", true, false) as Node3D @@ -21,6 +75,7 @@ func _ready() -> void: var catch_display := _visuals.find_child("CatchDisplay", true, false) as Node3D if catch_display != null: catch_display.visible = false + call_deferred("_refresh_pixelation_filter") func apply_appearance_profile(profile: Dictionary) -> void: @@ -29,14 +84,25 @@ func apply_appearance_profile(profile: Dictionary) -> void: func reset_view() -> void: - _preview_root.rotation.y = 0.0 + _preview_root.rotation.y = FRONT_FACING_YAW + _camera_yaw = 0.0 + _camera_pitch = DEFAULT_CAMERA_PITCH + _camera_distance = DEFAULT_CAMERA_DISTANCE + _apply_camera_orbit() func _process(delta: float) -> void: + _sync_world_lighting() if not has_focus(): return var axis := Input.get_axis("ui_left", "ui_right") - var right_stick := Input.get_joy_axis(0, JOY_AXIS_RIGHT_X) + var right_stick: float = ( + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X + ) + if _controller_mapping_manager != null + else Input.get_joy_axis(0, JOY_AXIS_RIGHT_X) + ) if absf(right_stick) > 0.2: axis = right_stick if absf(axis) > 0.1: @@ -44,13 +110,30 @@ func _process(delta: float) -> void: func _on_gui_input(event: InputEvent) -> void: - if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + if event is InputEventMouseButton and event.shift_pressed: + if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: + _zoom_preview(-CAMERA_ZOOM_STEP) + grab_focus() + accept_event() + return + if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: + _zoom_preview(CAMERA_ZOOM_STEP) + grab_focus() + accept_event() + return + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT: _dragging = event.pressed if event.pressed: grab_focus() accept_event() elif event is InputEventMouseMotion and _dragging: _rotate(event.relative.x * drag_sensitivity) + _camera_pitch = clampf( + _camera_pitch + event.relative.y * drag_sensitivity, + -0.55, + 0.55, + ) + _apply_camera_orbit() accept_event() @@ -60,4 +143,96 @@ func _notification(what: int) -> void: func _rotate(amount: float) -> void: - _preview_root.rotation.y = fposmod(_preview_root.rotation.y + amount, TAU) + _camera_yaw = fposmod(_camera_yaw - amount, TAU) + _apply_camera_orbit() + + +func _zoom_preview(amount: float) -> void: + if _preview_camera == null: + return + _camera_distance = clampf( + _camera_distance + amount, + MIN_CAMERA_DISTANCE, + MAX_CAMERA_DISTANCE, + ) + _apply_camera_orbit() + + +func _apply_camera_orbit() -> void: + if _preview_camera == null: + return + var horizontal_distance := cos(_camera_pitch) * _camera_distance + _preview_camera.position = Vector3( + sin(_camera_yaw) * horizontal_distance, + CAMERA_TARGET_HEIGHT + sin(_camera_pitch) * _camera_distance, + cos(_camera_yaw) * horizontal_distance, + ) + _preview_camera.look_at( + Vector3(0.0, CAMERA_TARGET_HEIGHT, 0.0), + Vector3.UP, + ) + + +func _sync_world_lighting() -> void: + # The customization viewport deliberately does not follow gameplay time of + # day. Keep this method as a compatibility seam for existing setup callers. + pass + + +func _configure_preview_lighting() -> void: + if _preview_environment == null or _preview_sun == null: + return + var environment := Environment.new() + environment.background_mode = Environment.BG_COLOR + environment.background_color = Color("082431") + environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR + environment.ambient_light_color = Color("b9d8d4") + environment.ambient_light_energy = 1.15 + environment.reflected_light_source = Environment.REFLECTION_SOURCE_DISABLED + _preview_environment.environment = environment + _preview_sun.rotation_degrees = Vector3(-35.0, -25.0, 0.0) + _preview_sun.light_color = Color("fff1d0") + _preview_sun.light_energy = 1.1 + _preview_sun.light_indirect_energy = 0.0 + _preview_sun.shadow_enabled = false + + +func _refresh_pixelation_filter() -> void: + if not is_node_ready() or _pixelation_material == null: + return + _pixelation_material.set_shader_parameter( + "logical_grid_size", + Vector2(calculate_pixelated_grid_size( + size, + Vector2(get_window().size), + _world_pixel_size, + )), + ) + + +static func calculate_pixelated_grid_size( + control_size: Vector2, + window_size: Vector2, + pixel_size: int, +) -> Vector2i: + var safe_window_size := Vector2( + maxf(window_size.x, 1.0), + maxf(window_size.y, 1.0), + ) + var world_grid_size: Vector2i = PlayerSettings.get_world_grid_size( + pixel_size, + Vector2i(roundi(safe_window_size.x), roundi(safe_window_size.y)), + ) + var world_pixel_extent: float = ( + safe_window_size.y / float(maxi(world_grid_size.y, 1)) + ) + var presentation_scale: float = UIReferencePresentationType.get_scale( + safe_window_size + ) + var grid_size: Vector2 = ( + control_size * presentation_scale / maxf(world_pixel_extent, 0.001) + ) + return Vector2i( + maxi(roundi(grid_size.x), 1), + maxi(roundi(grid_size.y), 1), + ) diff --git a/ui/profile_preview.tscn b/ui/profile_preview.tscn index 37be6fa..27313ec 100644 --- a/ui/profile_preview.tscn +++ b/ui/profile_preview.tscn @@ -1,45 +1,38 @@ [gd_scene load_steps=4 format=3] [ext_resource type="Script" path="res://ui/profile_preview.gd" id="1"] - -[sub_resource type="Environment" id="Environment"] -background_mode = 1 -background_color = Color(0.075, 0.13, 0.16, 1) -ambient_light_source = 3 -ambient_light_color = Color(0.72, 0.82, 0.86, 1) -ambient_light_energy = 0.65 +[ext_resource type="Shader" path="res://ui/profile_preview_pixelation.gdshader" id="2_pixelation"] [sub_resource type="World3D" id="PreviewWorld"] -environment = SubResource("Environment") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_pixelation"] +shader = ExtResource("2_pixelation") [node name="ProfilePreview" type="SubViewportContainer"] +material = SubResource("ShaderMaterial_pixelation") custom_minimum_size = Vector2(300, 220) +texture_filter = 1 stretch = true script = ExtResource("1") [node name="Viewport" type="SubViewport" parent="."] -transparent_bg = false +transparent_bg = true size = Vector2i(320, 330) render_target_update_mode = 4 world_3d = SubResource("PreviewWorld") [node name="WorldEnvironment" type="WorldEnvironment" parent="Viewport"] -environment = SubResource("Environment") +unique_name_in_owner = true [node name="KeyLight" type="DirectionalLight3D" parent="Viewport"] -rotation_degrees = Vector3(-38, -32, 0) -light_energy = 1.15 -shadow_enabled = true - -[node name="FillLight" type="OmniLight3D" parent="Viewport"] -position = Vector3(-1.8, 1.6, 2.2) -light_color = Color(0.72, 0.84, 1, 1) -omni_range = 5.0 -light_energy = 1.3 +unique_name_in_owner = true +light_energy = 0.1 +shadow_enabled = false [node name="PreviewRoot" type="Node3D" parent="Viewport"] unique_name_in_owner = true [node name="Camera3D" type="Camera3D" parent="Viewport"] +unique_name_in_owner = true position = Vector3(0, 0.95, 1.9) current = true diff --git a/ui/profile_preview_pixelation.gdshader b/ui/profile_preview_pixelation.gdshader new file mode 100644 index 0000000..27a4f07 --- /dev/null +++ b/ui/profile_preview_pixelation.gdshader @@ -0,0 +1,11 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform vec2 logical_grid_size = vec2(160.0, 165.0); + + +void fragment() { + vec2 safe_grid = max(logical_grid_size, vec2(1.0)); + vec2 snapped_uv = (floor(UV * safe_grid) + vec2(0.5)) / safe_grid; + COLOR = texture(TEXTURE, snapped_uv); +} diff --git a/ui/profile_preview_pixelation.gdshader.uid b/ui/profile_preview_pixelation.gdshader.uid new file mode 100644 index 0000000..dbf8e18 --- /dev/null +++ b/ui/profile_preview_pixelation.gdshader.uid @@ -0,0 +1 @@ +uid://dlbc0pxkqo028 diff --git a/ui/quick_radial_menu.gd b/ui/quick_radial_menu.gd new file mode 100644 index 0000000..8ae1720 --- /dev/null +++ b/ui/quick_radial_menu.gd @@ -0,0 +1,198 @@ +class_name QuickRadialMenu +extends Control + +signal action_selected(action_id: StringName) + +const BubbleButtonScene: PackedScene = preload( + "res://ui/components/bubble_menu/bubble_button.tscn" +) +const BubbleProfile: BubbleMenuProfile = preload( + "res://ui/components/bubble_menu/bubble_menu_profile.tres" +) +const RING_RADIUS: float = 172.0 +const BUBBLE_SIZE: Vector2 = Vector2(92.0, 88.0) +const CONTROLLER_SELECTION_DEADZONE: float = 0.35 +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const ACTIONS: Array[StringName] = [ + &"stuff", + &"logbook", + &"fishnet", + &"mail", + &"profile", + &"online", + &"paint", + &"chat", + &"freecam", + &"hud", +] +const LABELS: Array[String] = [ + "stuff", + "logbook", + "fishnet", + "mail", + "profile", + "online", + "paint", + "chat", + "freecam", + "hide hud", +] +const SECTOR_COUNT: int = 10 + +var _buttons: Array[BubbleButton] = [] +var _is_open: bool = false +var _selected_sector: int = 0 +var _controller_selection_mode: bool = false +var _controller_mapping_manager: ControllerMappingManagerType +var _hud_hidden: bool = false + + +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + + +func _ready() -> void: + visible = false + mouse_filter = Control.MOUSE_FILTER_IGNORE + for sector: int in SECTOR_COUNT: + var bubble: BubbleButton = BubbleButtonScene.instantiate() as BubbleButton + bubble.profile = BubbleProfile + bubble.focus_mode = Control.FOCUS_NONE + bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE + bubble.text = _label_for_sector(sector) + add_child(bubble) + _buttons.append(bubble) + _apply_selection_styles() + + +func handle_input(event: InputEvent, can_open: bool) -> bool: + if not event.is_action("open_quick_actions"): + return false + var key_event: InputEventKey = event as InputEventKey + if key_event != null and key_event.echo: + return false + if event.is_pressed(): + if _is_open or not can_open: + return _is_open + open_menu(event is InputEventJoypadButton) + return true + if not _is_open: + return false + var selected: int = _selected_sector + close_menu() + call_deferred("_emit_selected_action", ACTIONS[selected]) + return true + + +func open_menu(controller_selection: bool = false) -> void: + _is_open = true + _selected_sector = 0 + _controller_selection_mode = controller_selection + visible = true + _refresh_labels() + _layout_bubbles() + _apply_selection_styles() + + +func close_menu() -> void: + _is_open = false + visible = false + + +func is_open() -> bool: + return _is_open + + +func set_hud_hidden(hidden: bool) -> void: + _hud_hidden = hidden + _refresh_labels() + + +func _refresh_labels() -> void: + for sector: int in _buttons.size(): + _buttons[sector].text = _label_for_sector(sector) + + +func _label_for_sector(sector: int) -> String: + if ACTIONS[sector] == &"hud": + return "show hud" if _hud_hidden else "hide hud" + return LABELS[sector] + + +func _emit_selected_action(action_id: StringName) -> void: + action_selected.emit(action_id) + + +func _process(_delta: float) -> void: + if not _is_open: + return + _layout_bubbles() + _update_selection() + + +func _layout_bubbles() -> void: + var center: Vector2 = size * 0.5 + for sector: int in SECTOR_COUNT: + var angle: float = -PI * 0.5 + TAU * float(sector) / float(SECTOR_COUNT) + var bubble_center: Vector2 = center + Vector2.from_angle(angle) * RING_RADIUS + var bubble: BubbleButton = _buttons[sector] + bubble.position = bubble_center - BUBBLE_SIZE * 0.5 + bubble.size = BUBBLE_SIZE + bubble.pivot_offset = BUBBLE_SIZE * 0.5 + + +func _update_selection() -> void: + var stick: Vector2 = _get_selection_stick() + if stick.length() >= CONTROLLER_SELECTION_DEADZONE: + _select_sector_from_offset(stick) + return + if _controller_selection_mode: + return + _update_mouse_selection() + + +func _get_selection_stick() -> Vector2: + if _controller_mapping_manager != null: + return Vector2( + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_X + ), + _controller_mapping_manager.get_role_axis( + ControllerMappingManagerType.ROLE_RIGHT_STICK_Y + ), + ) + return Vector2( + Input.get_joy_axis(0, JOY_AXIS_RIGHT_X), + Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y), + ) + + +func _update_mouse_selection() -> void: + var offset: Vector2 = get_local_mouse_position() - size * 0.5 + if offset.length() < 24.0: + return + _select_sector_from_offset(offset) + + +func _select_sector_from_offset(offset: Vector2) -> void: + var half_sector: float = TAU / (float(SECTOR_COUNT) * 2.0) + var angle: float = fposmod(offset.angle() + PI * 0.5 + half_sector, TAU) + var sector: int = int(floor(angle / (TAU / float(SECTOR_COUNT)))) + if sector == _selected_sector: + return + _selected_sector = sector + _apply_selection_styles() + + +func _apply_selection_styles() -> void: + for sector: int in _buttons.size(): + var bubble: BubbleButton = _buttons[sector] + bubble.apply_profile() + if sector == _selected_sector: + bubble.add_theme_stylebox_override( + "normal", BubbleProfile.make_hover_style() + ) diff --git a/ui/quick_radial_menu.gd.uid b/ui/quick_radial_menu.gd.uid new file mode 100644 index 0000000..ee4327f --- /dev/null +++ b/ui/quick_radial_menu.gd.uid @@ -0,0 +1 @@ +uid://5nr1c8x881sw diff --git a/ui/quick_radial_menu.tscn b/ui/quick_radial_menu.tscn new file mode 100644 index 0000000..66a0f45 --- /dev/null +++ b/ui/quick_radial_menu.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://ui/quick_radial_menu.gd" id="1_quick"] + +[node name="QuickRadialMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +z_index = 90 +script = ExtResource("1_quick") diff --git a/ui/settings/settings_bubble_page.gd b/ui/settings/settings_bubble_page.gd index 1ccff30..dd01309 100644 --- a/ui/settings/settings_bubble_page.gd +++ b/ui/settings/settings_bubble_page.gd @@ -1,6 +1,10 @@ class_name SettingsBubblePage extends Control +const ControllerFocusNavigationType = preload( + "res://ui/controller_focus_navigation.gd" +) + @export var page_id: StringName @export var cluster_path: NodePath = ^"BubbleCluster" @export var bubble_paths: Array[NodePath] = [] @@ -15,7 +19,7 @@ extends Control var _cluster: BubbleCluster var _bubbles: Array[BubbleButton] = [] -var _focus_bubbles: Array[BubbleButton] = [] +var _focus_controls: Array[Control] = [] var _transition: Tween var _transition_generation: int = 0 var _resting_cluster_position: Vector2 = Vector2.ZERO @@ -29,9 +33,9 @@ func _ready() -> void: if bubble != null: _bubbles.append(bubble) for path: NodePath in focus_paths: - var bubble := get_node(path) as BubbleButton - if bubble != null: - _focus_bubbles.append(bubble) + var focus_control := get_node(path) as Control + if focus_control != null: + _focus_controls.append(focus_control) _cluster.configure(_bubbles) _configure_focus_order() resized.connect(_update_layout) @@ -169,23 +173,13 @@ func _update_layout() -> void: _cluster.position = _resting_cluster_position _cluster.size = field_size _cluster.apply_layout(field_size, compact) + ControllerFocusNavigationType.configure_spatial_neighbors(_focus_controls) func _configure_focus_order() -> void: for bubble: BubbleButton in _bubbles: bubble.focus_mode = Control.FOCUS_NONE - for index: int in _focus_bubbles.size(): - var bubble: BubbleButton = _focus_bubbles[index] - if index > 0: - bubble.focus_neighbor_top = bubble.get_path_to( - _focus_bubbles[index - 1] - ) - bubble.focus_neighbor_left = bubble.focus_neighbor_top - if index + 1 < _focus_bubbles.size(): - bubble.focus_neighbor_bottom = bubble.get_path_to( - _focus_bubbles[index + 1] - ) - bubble.focus_neighbor_right = bubble.focus_neighbor_bottom + ControllerFocusNavigationType.configure_spatial_neighbors(_focus_controls) func _set_interactive(interactive: bool) -> void: @@ -194,11 +188,11 @@ func _set_interactive(interactive: bool) -> void: if interactive else Control.MOUSE_FILTER_IGNORE ) - for bubble: BubbleButton in _focus_bubbles: - bubble.focus_mode = ( + for focus_control: Control in _focus_controls: + focus_control.focus_mode = ( Control.FOCUS_ALL if interactive else Control.FOCUS_NONE ) - bubble.mouse_filter = ( + focus_control.mouse_filter = ( Control.MOUSE_FILTER_STOP if interactive else Control.MOUSE_FILTER_IGNORE diff --git a/ui/settings_panel.gd b/ui/settings_panel.gd index 05b0c07..4a7ec9b 100644 --- a/ui/settings_panel.gd +++ b/ui/settings_panel.gd @@ -5,6 +5,7 @@ const MAX_PANEL_SIZE: Vector2 = Vector2(960.0, 700.0) const PANEL_EDGE_MARGIN: float = 16.0 const PAGE_ROOT: StringName = &"root" const PAGE_DISPLAY: StringName = &"display" +const PAGE_SOUND: StringName = &"sound" const PAGE_CONTROLS: StringName = &"controls" const PAGE_ACCESSIBILITY: StringName = &"accessibility" const PAGE_DATA: StringName = &"data" @@ -18,6 +19,12 @@ const PIXELATION_NAMES: PackedStringArray = [ const SettingsManagerType = preload( "res://settings/player_settings_manager.gd" ) +const ControllerMappingManagerType = preload( + "res://settings/controller_mapping_manager.gd" +) +const ControllerMappingPanelType = preload( + "res://ui/controller_mapping_panel.gd" +) signal applied signal closed @@ -34,6 +41,7 @@ enum PresentationMode { @onready var _root_page: SettingsBubblePage = %RootPage @onready var _display_page: SettingsBubblePage = %DisplayPage +@onready var _sound_page: SettingsBubblePage = %SoundPage @onready var _controls_page: SettingsBubblePage = %ControlsPage @onready var _accessibility_page: SettingsBubblePage = %AccessibilityPage @onready var _data_page: SettingsBubblePage = %DataPage @@ -42,12 +50,23 @@ enum PresentationMode { @onready var _world_value: BubbleButton = %WorldValue @onready var _ui_value: BubbleButton = %UIValue @onready var _chat_dock: BubbleButton = %ChatDock +@onready var _chat_mode: BubbleButton = %ChatMode @onready var _paint_dock: BubbleButton = %PaintDock +@onready var _fullscreen_toggle: BubbleButton = %FullscreenToggle @onready var _mouse_value: BubbleButton = %MouseValue @onready var _controller_value: BubbleButton = %ControllerValue @onready var _invert_y_toggle: BubbleButton = %InvertYToggle +@onready var _on_screen_keyboard_toggle: BubbleButton = %OnScreenKeyboardToggle @onready var _auto_click_toggle: BubbleButton = %AutoClickToggle @onready var _auto_click_interval: BubbleButton = %AutoClickIntervalValue +@onready var _master_volume_slider: HSlider = %MasterVolumeSlider +@onready var _music_volume_slider: HSlider = %MusicVolumeSlider +@onready var _effects_volume_slider: HSlider = %EffectsVolumeSlider +@onready var _environment_volume_slider: HSlider = %EnvironmentVolumeSlider +@onready var _master_volume_value: Label = %MasterVolumeValue +@onready var _music_volume_value: Label = %MusicVolumeValue +@onready var _effects_volume_value: Label = %EffectsVolumeValue +@onready var _environment_volume_value: Label = %EnvironmentVolumeValue @onready var _world_options: Array[BubbleButton] = [ %WorldLegible, @@ -77,6 +96,11 @@ var _auto_click_interval_value: float = 0.20 var _mouse_sensitivity: float = 0.005 var _controller_sensitivity: float = 2.5 var _invert_camera_y: bool = false +var _on_screen_keyboard_enabled: bool = false +var _master_volume: float = 1.0 +var _music_volume: float = 1.0 +var _effects_volume: float = 1.0 +var _environment_volume: float = 1.0 var _network_profile: NetworkProfilePreferences var _network_session: NetworkSession var _data_root: PlayerDataRoot @@ -84,6 +108,8 @@ var _identity_backups: IdentityBackupService var _player_identity: PlayerIdentityStore var _host_identity: HostIdentityStore var _interface_fonts: InterfaceFontController +var _controller_mapping_manager: ControllerMappingManagerType +var _controller_mapping_panel: ControllerMappingPanelType var _data_folder_dialog: FileDialog var _backup_file_dialog: FileDialog var _export_file_dialog: FileDialog @@ -100,11 +126,13 @@ func _ready() -> void: _pages = { PAGE_ROOT: _root_page, PAGE_DISPLAY: _display_page, + PAGE_SOUND: _sound_page, PAGE_CONTROLS: _controls_page, PAGE_ACCESSIBILITY: _accessibility_page, PAGE_DATA: _data_page, } %DisplayCategory.pressed.connect(_push_page.bind(PAGE_DISPLAY)) + %SoundCategory.pressed.connect(_push_page.bind(PAGE_SOUND)) %ControlsCategory.pressed.connect(_push_page.bind(PAGE_CONTROLS)) %AccessibilityCategory.pressed.connect( _push_page.bind(PAGE_ACCESSIBILITY) @@ -113,11 +141,14 @@ func _ready() -> void: %ApplySettingsButton.pressed.connect(_apply_settings) %RootBackButton.pressed.connect(close_panel) %DisplayBackButton.pressed.connect(handle_back) + %SoundBackButton.pressed.connect(handle_back) %ControlsBackButton.pressed.connect(handle_back) + %ControllerMapping.pressed.connect(_open_controller_mapping) %AccessibilityBackButton.pressed.connect(handle_back) %DataBackButton.pressed.connect(handle_back) %RootBackButton.gui_input.connect(_on_back_bubble_gui_input) %DisplayBackButton.gui_input.connect(_on_back_bubble_gui_input) + %SoundBackButton.gui_input.connect(_on_back_bubble_gui_input) %ControlsBackButton.gui_input.connect(_on_back_bubble_gui_input) %AccessibilityBackButton.gui_input.connect(_on_back_bubble_gui_input) %DataBackButton.gui_input.connect(_on_back_bubble_gui_input) @@ -125,6 +156,7 @@ func _ready() -> void: %ChangeDataFolder.pressed.connect(_choose_data_folder) %ExportPlayerIdentity.pressed.connect(_choose_identity_export.bind("player")) %ImportPlayerIdentity.pressed.connect(_choose_identity_import.bind("player")) + %CopyPlayerFingerprint.pressed.connect(_copy_player_fingerprint) %ExportHostIdentity.pressed.connect(_choose_identity_export.bind("host")) %ImportHostIdentity.pressed.connect(_choose_identity_import.bind("host")) for index: int in _world_options.size(): @@ -136,7 +168,9 @@ func _ready() -> void: _set_ui_pixelation.bind(index + 1) ) _chat_dock.pressed.connect(_toggle_chat_dock) + _chat_mode.pressed.connect(_toggle_chat_mode) _paint_dock.pressed.connect(_toggle_paint_dock) + _fullscreen_toggle.pressed.connect(_toggle_fullscreen) %MouseDecrease.pressed.connect(_adjust_mouse_sensitivity.bind(-1)) %MouseIncrease.pressed.connect(_adjust_mouse_sensitivity.bind(1)) _mouse_value.pressed.connect(_adjust_mouse_sensitivity.bind(1)) @@ -150,6 +184,7 @@ func _ready() -> void: _adjust_controller_sensitivity.bind(1) ) _invert_y_toggle.pressed.connect(_toggle_invert_y) + _on_screen_keyboard_toggle.pressed.connect(_toggle_on_screen_keyboard) _auto_click_toggle.pressed.connect(_toggle_auto_click) for control: Control in [_auto_click_toggle, _auto_click_interval]: control.mouse_entered.connect( @@ -163,6 +198,18 @@ func _ready() -> void: _auto_click_interval.pressed.connect( _adjust_auto_click_interval.bind(1) ) + _master_volume_slider.value_changed.connect( + _on_audio_volume_changed.bind(&"master") + ) + _music_volume_slider.value_changed.connect( + _on_audio_volume_changed.bind(&"music") + ) + _effects_volume_slider.value_changed.connect( + _on_audio_volume_changed.bind(&"effects") + ) + _environment_volume_slider.value_changed.connect( + _on_audio_volume_changed.bind(&"environment") + ) _mouse_value.gui_input.connect( _on_continuous_value_input.bind(&"mouse") ) @@ -178,7 +225,13 @@ func _ready() -> void: parent_control.resized.connect(_refresh_panel_size) for page: SettingsBubblePage in _pages.values(): page.hide_page() + _controller_mapping_panel = ControllerMappingPanelType.new() + add_child(_controller_mapping_panel) + _controller_mapping_panel.closed.connect( + _on_controller_mapping_panel_closed + ) _style_data_page() + _style_sound_page() call_deferred("_refresh_panel_size") @@ -190,6 +243,13 @@ func setup_network_profile( _network_session = session +func setup_controller_mapping( + mapping_manager: ControllerMappingManagerType, +) -> void: + _controller_mapping_manager = mapping_manager + _controller_mapping_panel.setup(_controller_mapping_manager) + + func setup_data_and_identity( data_root: PlayerDataRoot, identity_backups: IdentityBackupService, @@ -263,6 +323,11 @@ func close_panel(immediate: bool = false) -> void: func _finish_panel_close(applied_result: bool) -> void: _cancel_page_transition() + if ( + _controller_mapping_panel != null + and _controller_mapping_panel.is_open() + ): + _controller_mapping_panel.close_panel() for page: SettingsBubblePage in _pages.values(): page.hide_page() _page_stack.clear() @@ -276,6 +341,12 @@ func _finish_panel_close(applied_result: bool) -> void: func handle_back() -> void: + if ( + _controller_mapping_panel != null + and _controller_mapping_panel.is_open() + ): + _controller_mapping_panel.request_back() + return if _page_transition_active: return if _page_stack.size() > 1: @@ -284,10 +355,28 @@ func handle_back() -> void: close_panel() +func _open_controller_mapping() -> void: + if _controller_mapping_manager == null: + _feedback.text = "no controller mapping service is available" + return + _controller_mapping_panel.open_panel() + + +func _on_controller_mapping_panel_closed() -> void: + %ControllerMapping.grab_focus() + + func get_active_page_id() -> StringName: return _page_stack.back() if not _page_stack.is_empty() else StringName() +func is_controller_mapping_capturing() -> bool: + return ( + _controller_mapping_panel != null + and _controller_mapping_panel.is_capturing() + ) + + func _push_page(page_id: StringName) -> void: if ( _page_transition_active @@ -397,6 +486,16 @@ func _refresh_data_page() -> void: _data_root.override_active or (_network_session != null and _network_session.is_session_active()) ) + var fingerprint: String = ( + _player_identity.fingerprint if _player_identity != null else "" + ) + %CopyPlayerFingerprint.disabled = not NetworkIdentityCrypto.valid_fingerprint( + fingerprint + ) + %CopyPlayerFingerprint.text = ( + "Copy Player Fingerprint · %s" + % NetworkIdentityCrypto.compact_suffix(fingerprint) + ) func _open_data_folder() -> void: @@ -404,6 +503,17 @@ func _open_data_folder() -> void: _feedback.text = "Could not open the data folder." +func _copy_player_fingerprint() -> void: + var fingerprint: String = ( + _player_identity.fingerprint if _player_identity != null else "" + ) + if not NetworkIdentityCrypto.valid_fingerprint(fingerprint): + _feedback.text = "Player identity is unavailable." + return + DisplayServer.clipboard_set(fingerprint) + _feedback.text = "Full player fingerprint copied for server operator setup." + + func _choose_data_folder() -> void: if _data_root == null or _data_root.override_active: _feedback.text = "The data folder is externally managed." @@ -420,7 +530,7 @@ func _choose_data_folder() -> void: _interface_fonts.apply_utility_theme(_data_folder_dialog) add_child(_data_folder_dialog) _data_folder_dialog.current_dir = _data_root.root_path.get_base_dir() - _data_folder_dialog.popup_centered_ratio(0.75) + _interface_fonts.popup_file_dialog(_data_folder_dialog) func _change_data_folder(path: String) -> void: @@ -488,7 +598,7 @@ func _choose_identity_export(identity_type: String) -> void: add_child(_export_file_dialog) _export_file_dialog.current_dir = suggested.get_base_dir() _export_file_dialog.current_file = suggested.get_file() - _export_file_dialog.popup_centered_ratio(0.75) + _interface_fonts.popup_file_dialog(_export_file_dialog) func _identity_export_file_selected(path: String) -> void: @@ -513,7 +623,7 @@ func _choose_identity_import(identity_type: String) -> void: _interface_fonts.apply_utility_theme(_backup_file_dialog) add_child(_backup_file_dialog) _backup_file_dialog.current_dir = _data_root.identity_backup_directory() - _backup_file_dialog.popup_centered_ratio(0.75) + _interface_fonts.popup_file_dialog(_backup_file_dialog) func _identity_import_file_selected(path: String) -> void: @@ -632,7 +742,7 @@ func _identity_operation_allowed() -> bool: return true -func _on_identity_operation_finished(success: bool, message: String) -> void: +func _on_identity_operation_finished(_success: bool, message: String) -> void: _feedback.text = message @@ -652,6 +762,11 @@ func _apply_settings() -> void: edited.mouse_camera_sensitivity = _mouse_sensitivity edited.controller_camera_sensitivity = _controller_sensitivity edited.invert_camera_y = _invert_camera_y + edited.on_screen_keyboard_enabled = _on_screen_keyboard_enabled + edited.master_volume = _master_volume + edited.music_volume = _music_volume + edited.effects_volume = _effects_volume + edited.environment_volume = _environment_volume if _settings_manager.apply_settings(edited): if ( _presentation_mode == PresentationMode.TITLE_EMBEDDED @@ -673,6 +788,18 @@ func _load_controls() -> void: _mouse_sensitivity = settings.mouse_camera_sensitivity _controller_sensitivity = settings.controller_camera_sensitivity _invert_camera_y = settings.invert_camera_y + _on_screen_keyboard_enabled = settings.on_screen_keyboard_enabled + _master_volume = settings.master_volume + _music_volume = settings.music_volume + _effects_volume = settings.effects_volume + _environment_volume = settings.environment_volume + _master_volume_slider.set_value_no_signal(_master_volume * 100.0) + _music_volume_slider.set_value_no_signal(_music_volume * 100.0) + _effects_volume_slider.set_value_no_signal(_effects_volume * 100.0) + _environment_volume_slider.set_value_no_signal( + _environment_volume * 100.0 + ) + _settings_manager.restore_audio_levels() _refresh_value_labels() @@ -689,9 +816,15 @@ func _refresh_value_labels() -> void: + _pixel_size_label(settings.ui_pixel_size) ) _chat_dock.text = "chat dock\n" + ("right" if settings.chat_dock_right else "left") + _chat_mode.text = ( + "chat mode\n" + ("mobile" if settings.chat_mobile_mode else "desktop") + ) _paint_dock.text = ( "paint dock\n" + ("right" if settings.paint_dock_right else "left") ) + _fullscreen_toggle.text = ( + "fullscreen\n" + ("on" if settings.fullscreen_enabled else "off") + ) _mouse_value.text = "mouse\nsensitivity\n%.4f" % _mouse_sensitivity _controller_value.text = ( "controller\nsensitivity\n%.1f" % _controller_sensitivity @@ -700,6 +833,10 @@ func _refresh_value_labels() -> void: "invert\nvertical\ncamera\n" + ("on" if _invert_camera_y else "off") ) + _on_screen_keyboard_toggle.text = ( + "on-screen\nkeyboard\n" + + ("on" if _on_screen_keyboard_enabled else "off") + ) _auto_click_toggle.text = ( "accessibility\nauto-click\n" + ("on" if _auto_click_enabled else "off") @@ -707,6 +844,7 @@ func _refresh_value_labels() -> void: _auto_click_interval.text = ( "auto-click\ninterval\n%.2f s" % _auto_click_interval_value ) + _refresh_audio_value_labels() for index: int in _world_options.size(): _world_options[index].button_pressed = ( index + 1 == settings.world_pixel_size @@ -739,6 +877,49 @@ func _style_data_page() -> void: ) +func _style_sound_page() -> void: + UtilityPageStyle.apply_page(_sound_page) + var paper := _sound_page.get_node("Paper") as PanelContainer + paper.add_theme_stylebox_override( + "panel", UtilityPageStyle.panel_style() + ) + var content := _sound_page.get_node("Paper/Content") as VBoxContainer + for node: Node in content.find_children("*", "Label", true, false): + (node as Label).add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + + +func _on_audio_volume_changed(value: float, channel: StringName) -> void: + var linear_volume: float = clampf(value / 100.0, 0.0, 1.0) + match channel: + &"master": + _master_volume = linear_volume + &"music": + _music_volume = linear_volume + &"effects": + _effects_volume = linear_volume + &"environment": + _environment_volume = linear_volume + _refresh_audio_value_labels() + if _settings_manager != null: + _settings_manager.preview_audio_levels( + _master_volume, + _music_volume, + _effects_volume, + _environment_volume, + ) + + +func _refresh_audio_value_labels() -> void: + _master_volume_value.text = "%d%%" % roundi(_master_volume * 100.0) + _music_volume_value.text = "%d%%" % roundi(_music_volume * 100.0) + _effects_volume_value.text = "%d%%" % roundi(_effects_volume * 100.0) + _environment_volume_value.text = ( + "%d%%" % roundi(_environment_volume * 100.0) + ) + + func _set_world_pixelation(pixel_size: int) -> void: if _settings_manager == null: return @@ -774,6 +955,28 @@ func _toggle_chat_dock() -> void: _refresh_value_labels() +func _toggle_chat_mode() -> void: + if _settings_manager == null: + return + var edited: PlayerSettings = _settings_manager.current_settings.copy() + edited.chat_mobile_mode = not edited.chat_mobile_mode + if not _settings_manager.apply_settings(edited): + _feedback.text = "failed to save chat mode setting." + return + _refresh_value_labels() + + +func _toggle_fullscreen() -> void: + if _settings_manager == null: + return + var edited: PlayerSettings = _settings_manager.current_settings.copy() + edited.fullscreen_enabled = not edited.fullscreen_enabled + if not _settings_manager.apply_settings(edited): + _feedback.text = "failed to save fullscreen setting." + return + _refresh_value_labels() + + func _toggle_paint_dock() -> void: if _settings_manager == null: return @@ -817,6 +1020,11 @@ func _toggle_invert_y() -> void: _refresh_value_labels() +func _toggle_on_screen_keyboard() -> void: + _on_screen_keyboard_enabled = not _on_screen_keyboard_enabled + _refresh_value_labels() + + func _toggle_auto_click() -> void: _auto_click_enabled = not _auto_click_enabled _refresh_value_labels() diff --git a/ui/settings_panel.tscn b/ui/settings_panel.tscn index 90e2a13..df2e261 100644 --- a/ui/settings_panel.tscn +++ b/ui/settings_panel.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3] +[gd_scene load_steps=9 format=3] [ext_resource type="Script" path="res://ui/settings_panel.gd" id="1_panel"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] @@ -6,6 +6,8 @@ [ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_button.tscn" id="4_bubble"] [ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_cluster.gd" id="5_cluster"] [ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="6_profile"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_dark.png" id="7_undo_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/check_mark_dark.png" id="8_check_mark_dark"] [node name="SettingsPanel" type="Control"] visible = false @@ -39,8 +41,8 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("3_page") page_id = &"root" -bubble_paths = Array[NodePath]([NodePath("BubbleCluster/DisplayCategory"), NodePath("BubbleCluster/ControlsCategory"), NodePath("BubbleCluster/AccessibilityCategory"), NodePath("BubbleCluster/DataCategory"), NodePath("BubbleCluster/ApplySettingsButton"), NodePath("BubbleCluster/RootBackButton")]) -focus_paths = Array[NodePath]([NodePath("BubbleCluster/DisplayCategory"), NodePath("BubbleCluster/ControlsCategory"), NodePath("BubbleCluster/AccessibilityCategory"), NodePath("BubbleCluster/DataCategory"), NodePath("BubbleCluster/ApplySettingsButton"), NodePath("BubbleCluster/RootBackButton")]) +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/DisplayCategory"), NodePath("BubbleCluster/SoundCategory"), NodePath("BubbleCluster/ControlsCategory"), NodePath("BubbleCluster/AccessibilityCategory"), NodePath("BubbleCluster/DataCategory"), NodePath("BubbleCluster/ApplySettingsButton"), NodePath("BubbleCluster/RootBackButton")]) +focus_paths = Array[NodePath]([NodePath("BubbleCluster/DisplayCategory"), NodePath("BubbleCluster/SoundCategory"), NodePath("BubbleCluster/ControlsCategory"), NodePath("BubbleCluster/AccessibilityCategory"), NodePath("BubbleCluster/DataCategory"), NodePath("BubbleCluster/ApplySettingsButton"), NodePath("BubbleCluster/RootBackButton")]) initial_focus_path = NodePath("BubbleCluster/DisplayCategory") back_focus_path = NodePath("BubbleCluster/RootBackButton") compact_maximum_layout_size = Vector2(544, 400) @@ -86,6 +88,18 @@ minimum_font_size = 17 maximum_font_size = 25 motion_phase = 1.45 +[node name="SoundCategory" parent="RootPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +text = "sound" +neutral_size = Vector2(144, 136) +desktop_anchor = Vector2(100, 180) +compact_anchor = Vector2(90, 150) +compact_minimum_size = Vector2(118, 112) +minimum_font_size = 17 +maximum_font_size = 25 +motion_phase = 2.0 + [node name="AccessibilityCategory" parent="RootPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) @@ -109,7 +123,9 @@ offset_left = 474.0 offset_top = 308.0 offset_right = 586.0 offset_bottom = 412.0 -text = "apply" +icon = ExtResource("8_check_mark_dark") +tooltip_text = "apply" +accessibility_name = "apply" neutral_size = Vector2(112, 104) desktop_anchor = Vector2(511, 332) compact_anchor = Vector2(470, 330) @@ -137,7 +153,9 @@ offset_left = 70.0 offset_top = 343.0 offset_right = 170.0 offset_bottom = 437.0 -text = "back" +icon = ExtResource("7_undo_dark") +tooltip_text = "back" +accessibility_name = "back" neutral_size = Vector2(100, 94) desktop_anchor = Vector2(220, 300) compact_anchor = Vector2(155, 275) @@ -157,8 +175,8 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("3_page") page_id = &"display" -bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")]) -focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")]) +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/ChatMode"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/FullscreenToggle"), NodePath("BubbleCluster/DisplayBackButton")]) +focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/ChatMode"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/FullscreenToggle"), NodePath("BubbleCluster/DisplayBackButton")]) initial_focus_path = NodePath("BubbleCluster/WorldRetro") back_focus_path = NodePath("BubbleCluster/DisplayBackButton") compact_maximum_layout_size = Vector2(544, 400) @@ -337,36 +355,239 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) text = "chat dock\nleft" neutral_size = Vector2(96, 96) -desktop_anchor = Vector2(190, 435) -compact_anchor = Vector2(160, 378) +desktop_anchor = Vector2(70, 435) +compact_anchor = Vector2(55, 378) compact_minimum_size = Vector2(82, 82) minimum_font_size = 12 maximum_font_size = 16 motion_phase = 1.1 +[node name="ChatMode" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +text = "chat mode\ndesktop" +neutral_size = Vector2(96, 96) +desktop_anchor = Vector2(215, 435) +compact_anchor = Vector2(180, 378) +compact_minimum_size = Vector2(82, 82) +minimum_font_size = 12 +maximum_font_size = 16 +motion_phase = 1.4 + [node name="PaintDock" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) text = "paint dock\nright" neutral_size = Vector2(96, 96) -desktop_anchor = Vector2(530, 435) -compact_anchor = Vector2(448, 378) +desktop_anchor = Vector2(360, 435) +compact_anchor = Vector2(305, 378) compact_minimum_size = Vector2(82, 82) minimum_font_size = 12 maximum_font_size = 16 -motion_phase = 1.65 +motion_phase = 1.7 + +[node name="FullscreenToggle" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +text = "fullscreen\noff" +neutral_size = Vector2(104, 96) +desktop_anchor = Vector2(505, 435) +compact_anchor = Vector2(430, 378) +compact_minimum_size = Vector2(88, 82) +minimum_font_size = 11 +maximum_font_size = 15 +motion_phase = 1.85 [node name="DisplayBackButton" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) text = "back" neutral_size = Vector2(96, 90) -desktop_anchor = Vector2(350, 445) -compact_anchor = Vector2(315, 390) +desktop_anchor = Vector2(650, 435) +compact_anchor = Vector2(555, 378) compact_minimum_size = Vector2(82, 78) minimum_font_size = 13 maximum_font_size = 16 -motion_phase = 2.2 +motion_phase = 2.15 + +[node name="SoundPage" type="Control" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("3_page") +page_id = &"sound" +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/SoundBackButton")]) +focus_paths = Array[NodePath]([NodePath("Paper/Content/VolumeGrid/MasterVolumeSlider"), NodePath("Paper/Content/VolumeGrid/MusicVolumeSlider"), NodePath("Paper/Content/VolumeGrid/EffectsVolumeSlider"), NodePath("Paper/Content/VolumeGrid/EnvironmentVolumeSlider"), NodePath("BubbleCluster/SoundBackButton")]) +initial_focus_path = NodePath("Paper/Content/VolumeGrid/MasterVolumeSlider") +back_focus_path = NodePath("BubbleCluster/SoundBackButton") +compact_maximum_layout_size = Vector2(544, 400) + +[node name="BubbleCluster" type="Control" parent="SoundPage"] +layout_mode = 0 +offset_right = 720.0 +offset_bottom = 520.0 +script = ExtResource("5_cluster") +profile = ExtResource("6_profile") +desktop_reference_size = Vector2(720, 520) +compact_reference_size = Vector2(608, 448) + +[node name="Paper" type="PanelContainer" parent="SoundPage"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -310.0 +offset_top = -230.0 +offset_right = 310.0 +offset_bottom = 205.0 +grow_horizontal = 2 +grow_vertical = 2 +z_index = 1 + +[node name="Content" type="VBoxContainer" parent="SoundPage/Paper"] +layout_mode = 2 +theme_override_constants/separation = 18 + +[node name="Heading" type="Label" parent="SoundPage/Paper/Content"] +layout_mode = 2 +theme_override_font_sizes/font_size = 28 +text = "sound" +horizontal_alignment = 1 + +[node name="Helper" type="Label" parent="SoundPage/Paper/Content"] +layout_mode = 2 +text = "Adjust the mix. Changes are saved when you apply settings." +horizontal_alignment = 1 + +[node name="VolumeGrid" type="GridContainer" parent="SoundPage/Paper/Content"] +layout_mode = 2 +theme_override_constants/h_separation = 16 +theme_override_constants/v_separation = 18 +columns = 3 + +[node name="MasterLabel" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +custom_minimum_size = Vector2(118, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "master" +vertical_alignment = 1 + +[node name="MasterVolumeSlider" type="HSlider" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(350, 42) +layout_mode = 2 +focus_mode = 2 +tooltip_text = "master volume" +max_value = 100.0 +step = 1.0 +value = 100.0 + +[node name="MasterVolumeValue" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "100%" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="MusicLabel" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +custom_minimum_size = Vector2(118, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "music" +vertical_alignment = 1 + +[node name="MusicVolumeSlider" type="HSlider" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(350, 42) +layout_mode = 2 +focus_mode = 2 +tooltip_text = "music volume" +max_value = 100.0 +step = 1.0 +value = 100.0 + +[node name="MusicVolumeValue" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "100%" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="EffectsLabel" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +custom_minimum_size = Vector2(118, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "effects" +vertical_alignment = 1 + +[node name="EffectsVolumeSlider" type="HSlider" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(350, 42) +layout_mode = 2 +focus_mode = 2 +tooltip_text = "effects volume" +max_value = 100.0 +step = 1.0 +value = 100.0 + +[node name="EffectsVolumeValue" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "100%" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="EnvironmentLabel" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +custom_minimum_size = Vector2(118, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "environment" +vertical_alignment = 1 + +[node name="EnvironmentVolumeSlider" type="HSlider" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(350, 42) +layout_mode = 2 +focus_mode = 2 +tooltip_text = "environment volume" +max_value = 100.0 +step = 1.0 +value = 100.0 + +[node name="EnvironmentVolumeValue" type="Label" parent="SoundPage/Paper/Content/VolumeGrid"] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 42) +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "100%" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="SoundBackButton" parent="SoundPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +z_index = 4 +layout_mode = 0 +text = "back" +neutral_size = Vector2(96, 90) +desktop_anchor = Vector2(60, 460) +compact_anchor = Vector2(55, 410) +compact_minimum_size = Vector2(82, 78) +minimum_font_size = 13 +maximum_font_size = 16 +motion_phase = 5.0 [node name="ControlsPage" type="Control" parent="."] unique_name_in_owner = true @@ -379,8 +600,8 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("3_page") page_id = &"controls" -bubble_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControlsBackButton")]) -focus_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/ControlsBackButton")]) +bubble_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/OnScreenKeyboardToggle"), NodePath("BubbleCluster/ControllerMapping"), NodePath("BubbleCluster/ControlsBackButton")]) +focus_paths = Array[NodePath]([NodePath("BubbleCluster/MouseDecrease"), NodePath("BubbleCluster/MouseValue"), NodePath("BubbleCluster/MouseIncrease"), NodePath("BubbleCluster/ControllerDecrease"), NodePath("BubbleCluster/ControllerValue"), NodePath("BubbleCluster/ControllerIncrease"), NodePath("BubbleCluster/InvertYToggle"), NodePath("BubbleCluster/OnScreenKeyboardToggle"), NodePath("BubbleCluster/ControllerMapping"), NodePath("BubbleCluster/ControlsBackButton")]) initial_focus_path = NodePath("BubbleCluster/MouseValue") back_focus_path = NodePath("BubbleCluster/ControlsBackButton") @@ -470,24 +691,48 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) text = "invert\nvertical\ncamera\noff" neutral_size = Vector2(176, 170) -desktop_anchor = Vector2(525, 230) -compact_anchor = Vector2(485, 225) compact_minimum_size = Vector2(170, 164) minimum_font_size = 14 maximum_font_size = 24 +desktop_anchor = Vector2(525, 105) +compact_anchor = Vector2(485, 95) motion_phase = 4.5 +[node name="OnScreenKeyboardToggle" parent="ControlsPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +text = "on-screen\nkeyboard\noff" +neutral_size = Vector2(170, 150) +compact_minimum_size = Vector2(154, 136) +minimum_font_size = 14 +maximum_font_size = 22 +desktop_anchor = Vector2(525, 255) +compact_anchor = Vector2(485, 235) +motion_phase = 4.7 + +[node name="ControllerMapping" parent="ControlsPage/BubbleCluster" instance=ExtResource("4_bubble")] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 0) +text = "map\ncontroller" +neutral_size = Vector2(160, 150) +desktop_anchor = Vector2(525, 390) +compact_anchor = Vector2(485, 355) +compact_minimum_size = Vector2(148, 138) +minimum_font_size = 14 +maximum_font_size = 23 +motion_phase = 4.9 + [node name="ControlsBackButton" parent="ControlsPage/BubbleCluster" instance=ExtResource("4_bubble")] unique_name_in_owner = true custom_minimum_size = Vector2(0, 0) text = "back" neutral_size = Vector2(100, 94) -desktop_anchor = Vector2(525, 425) -compact_anchor = Vector2(540, 395) +desktop_anchor = Vector2(635, 470) +compact_anchor = Vector2(555, 420) compact_minimum_size = Vector2(84, 80) minimum_font_size = 14 maximum_font_size = 17 -motion_phase = 5.2 +motion_phase = 5.5 [node name="AccessibilityPage" type="Control" parent="."] unique_name_in_owner = true @@ -668,6 +913,11 @@ layout_mode = 2 text = "Active identity keys stay on this device.\nEncrypted backups can be stored in your synced data folder.\nDo not play the same profile on two devices at the same time." autowrap_mode = 2 +[node name="CopyPlayerFingerprint" type="Button" parent="DataPage/Paper/Content"] +unique_name_in_owner = true +layout_mode = 2 +text = "Copy Player Fingerprint" + [node name="IdentityGrid" type="GridContainer" parent="DataPage/Paper/Content"] layout_mode = 2 columns = 2 diff --git a/ui/surface_drawing_toolbar.gd b/ui/surface_drawing_toolbar.gd index 0c13705..e6c8efa 100644 --- a/ui/surface_drawing_toolbar.gd +++ b/ui/surface_drawing_toolbar.gd @@ -2,7 +2,16 @@ class_name SurfaceDrawingToolbar extends Control const UtilityPageStyleType = preload("res://ui/utility_page_style.gd") -const TOOLBAR_WIDTH: float = 580.0 +const MARKER_MODE_ICON: Texture2D = preload( + "res://items/icons/art/art_kit_marker.png" +) +const MARKER_MODE_SHADER: Shader = preload( + "res://ui/art_kit_marker_icon.gdshader" +) +const GRID_MODE_ICON: Texture2D = preload( + "res://items/icons/art/art_kit_grid_light.png" +) +const TOOLBAR_WIDTH: float = 510.0 const TOOLBAR_HEIGHT: float = 373.0 const COLOR_RAIL_WIDTH: float = 46.0 @@ -17,12 +26,12 @@ const COLOR_RAIL_WIDTH: float = 46.0 @onready var _hide_guide_button: Button = %HideGuideButton @onready var _restore_guide_button: Button = %RestoreGuideButton @onready var _finalize_guide_button: Button = %FinalizeGuideButton -@onready var _close_button: Button = %CloseButton var _service: NetworkSurfaceDrawingService var _unlocks: PlayerArtUnlocks var _color_buttons: Dictionary[StringName, Button] = {} var _dock_right: bool = true +var _marker_mode_material: ShaderMaterial func _ready() -> void: @@ -38,6 +47,16 @@ func _ready() -> void: _make_button_round(button, 22) _mode_button.custom_minimum_size = Vector2(48, 48) _make_button_round(_mode_button, 24) + _enlarge_action_icon(_mode_button) + _marker_mode_material = ShaderMaterial.new() + _marker_mode_material.shader = MARKER_MODE_SHADER + _marker_mode_material.set_shader_parameter( + "marker_color", + SurfaceDrawingPalette.get_color(SurfaceDrawingPalette.DEFAULT_COLOR_ID), + ) + _mode_button.material = _marker_mode_material + for button: Button in _action_buttons(): + _enlarge_action_icon(button) _mode_button.pressed.connect(_toggle_mode) _eraser_button.pressed.connect(_toggle_eraser) _undo_button.pressed.connect(_undo_last_stroke) @@ -50,7 +69,6 @@ func _ready() -> void: _finalize_guide_button.pressed.connect( _arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.FINALIZE) ) - _close_button.pressed.connect(_close_toolbar) _brush_option.item_selected.connect(_select_brush) _grid_option.item_selected.connect(_select_grid) _build_options() @@ -75,7 +93,6 @@ func _action_buttons() -> Array[Button]: _hide_guide_button, _restore_guide_button, _finalize_guide_button, - _close_button, ] @@ -91,6 +108,25 @@ func _make_button_round(button: Button, radius: int) -> void: button.add_theme_stylebox_override(style_name, style) +func _enlarge_action_icon(button: Button) -> void: + # The shared ocean button uses generous text padding, leaving only a + # 20-pixel icon area in these compact bubbles. Keep the 48x44 control and + # hitbox intact while giving icon-only actions a full 40x40 presentation. + button.add_theme_constant_override("icon_max_width", 40) + for style_name: StringName in [ + &"normal", &"hover", &"pressed", &"focus", &"disabled", + ]: + var existing: StyleBox = button.get_theme_stylebox(style_name) + var style := existing.duplicate() as StyleBoxFlat + if style == null: + continue + style.content_margin_left = 4.0 + style.content_margin_right = 4.0 + style.content_margin_top = 2.0 + style.content_margin_bottom = 2.0 + button.add_theme_stylebox_override(style_name, style) + + func setup( service: NetworkSurfaceDrawingService, unlocks: PlayerArtUnlocks, @@ -125,6 +161,8 @@ func _apply_dock_side() -> void: offset_right = 0.0 if _dock_right else TOOLBAR_WIDTH offset_top = 0.0 offset_bottom = TOOLBAR_HEIGHT + _top_panel.offset_left = 0.0 + _top_panel.offset_right = TOOLBAR_WIDTH _color_panel.offset_left = ( TOOLBAR_WIDTH - COLOR_RAIL_WIDTH if _dock_right else 0.0 ) @@ -255,11 +293,6 @@ func _arm_guide_action(action: int) -> void: _service.arm_guide_action(action) -func _close_toolbar() -> void: - if _service != null: - _service.deactivate() - - func _on_unlocks_changed(_unlock_mask: int) -> void: _refresh_unlocks() @@ -268,7 +301,7 @@ func _on_service_state_changed( is_active: bool, mode_name: String, _color_name: String, - _color_value: Color, + color_value: Color, brush_size: int, grid_size: int, _status: String, @@ -276,7 +309,15 @@ func _on_service_state_changed( visible = is_active if not is_active: return - _mode_button.text = "▦" if mode_name == "place grid" else "●" + _mode_button.icon = ( + GRID_MODE_ICON if mode_name == "place grid" else MARKER_MODE_ICON + ) + _marker_mode_material.set_shader_parameter("marker_color", color_value) + _mode_button.accessibility_name = ( + "switch to marker mode" + if mode_name == "place grid" + else "switch to grid mode" + ) _mode_button.tooltip_text = ( "Switch to marker mode" if mode_name == "place grid" diff --git a/ui/surface_drawing_toolbar.tscn b/ui/surface_drawing_toolbar.tscn index 61db805..448cae2 100644 --- a/ui/surface_drawing_toolbar.tscn +++ b/ui/surface_drawing_toolbar.tscn @@ -1,7 +1,17 @@ -[gd_scene load_steps=3 format=3] +[gd_scene load_steps=11 format=3] [ext_resource type="Script" path="res://ui/surface_drawing_toolbar.gd" id="1_script"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_light.png" id="3_undo_light"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_eraser.png" id="4_art_kit_eraser"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_marker.png" id="5_art_kit_marker"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_light.png" id="6_grid_light"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_hidden_light.png" id="7_grid_hidden"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_restore_light.png" id="8_grid_restore"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_finish_light.png" id="9_grid_finish"] + +[sub_resource type="ButtonGroup" id="GuideActionGroup"] +allow_unpress = true [node name="SurfaceDrawingToolbar" type="Control"] visible = false @@ -10,7 +20,7 @@ layout_mode = 0 anchors_preset = 1 anchor_left = 1.0 anchor_right = 1.0 -offset_left = -580.0 +offset_left = -510.0 offset_bottom = 373.0 grow_horizontal = 0 mouse_filter = 2 @@ -20,19 +30,13 @@ script = ExtResource("1_script") [node name="TopPanel" type="PanelContainer" parent="."] unique_name_in_owner = true layout_mode = 0 -offset_right = 580.0 +offset_right = 510.0 offset_bottom = 60.0 [node name="Top" type="HBoxContainer" parent="TopPanel"] layout_mode = 2 theme_override_constants/separation = 5 -[node name="ModeButton" type="Button" parent="TopPanel/Top"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "switch between grid and marker mode" -text = "▦" - [node name="BrushOption" type="OptionButton" parent="TopPanel/Top"] unique_name_in_owner = true custom_minimum_size = Vector2(80, 48) @@ -48,49 +52,70 @@ tooltip_text = "grid size" [node name="EraserButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 +texture_filter = 1 tooltip_text = "toggle eraser" toggle_mode = true -text = "⌫" +accessibility_name = "toggle eraser" +icon = ExtResource("4_art_kit_eraser") +expand_icon = true [node name="UndoButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 +texture_filter = 1 tooltip_text = "undo last stroke" -text = "↶" +accessibility_name = "undo last stroke" +icon = ExtResource("3_undo_light") +expand_icon = true [node name="HideGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "hide grid on next click" +texture_filter = 1 +tooltip_text = "toggle hide grid mode" toggle_mode = true -text = "◌" +button_group = SubResource("GuideActionGroup") +accessibility_name = "toggle hide grid mode" +icon = ExtResource("7_grid_hidden") +expand_icon = true [node name="RestoreGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "restore hidden grid on next click" +texture_filter = 1 +tooltip_text = "toggle show grid mode" toggle_mode = true -text = "▤" +button_group = SubResource("GuideActionGroup") +accessibility_name = "toggle show grid mode" +icon = ExtResource("8_grid_restore") +expand_icon = true [node name="FinalizeGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "finish grid on next click" +texture_filter = 1 +tooltip_text = "toggle finalize grid mode" toggle_mode = true -text = "✓" +button_group = SubResource("GuideActionGroup") +accessibility_name = "toggle finalize grid mode" +icon = ExtResource("9_grid_finish") +expand_icon = true -[node name="CloseButton" type="Button" parent="TopPanel/Top"] +[node name="ModeButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "close art tools" -text = "×" +texture_filter = 1 +tooltip_text = "switch between grid and marker mode" +accessibility_name = "switch to grid mode" +icon = ExtResource("5_art_kit_marker") +expand_icon = true [node name="ColorPanel" type="PanelContainer" parent="."] unique_name_in_owner = true layout_mode = 0 -offset_left = 534.0 +offset_left = 464.0 offset_top = 54.0 -offset_right = 580.0 +offset_right = 510.0 offset_bottom = 373.0 [node name="ColorList" type="VBoxContainer" parent="ColorPanel"] diff --git a/ui/the_net_page.gd b/ui/the_net_page.gd new file mode 100644 index 0000000..ff83a11 --- /dev/null +++ b/ui/the_net_page.gd @@ -0,0 +1,517 @@ +class_name TheNetPage +extends Control + +const CurrencyPresentationType = preload( + "res://ui/currency_presentation.gd" +) + +enum View { + DAILY, + LIFETIME, + PAYMENTS, +} + +var _jobs: PlayerJobService +var _world_time: WorldTimeService +var _header: Label +var _refresh_label: Label +var _forecast_list: HBoxContainer +var _tabs: HBoxContainer +var _list: VBoxContainer +var _status: Label +var _current_view: View = View.DAILY +var _forecast_start_index: int = -1 +var _active: bool = false +var _interactive: bool = false + + +func _ready() -> void: + set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + UtilityPageStyle.apply_page(self) + _build_laptop() + set_process(false) + + +func setup(jobs: PlayerJobService, world_time: WorldTimeService) -> void: + _jobs = jobs + _world_time = world_time + _jobs.changed.connect(_refresh) + _jobs.status_changed.connect(_on_status_changed) + _refresh() + + +func activate() -> void: + _active = true + set_process(true) + _refresh() + focus_initial() + + +func deactivate() -> void: + _active = false + set_process(false) + set_interactive(false) + _status.text = "" + + +func set_interactive(interactive: bool) -> void: + _interactive = interactive + mouse_filter = ( + Control.MOUSE_FILTER_PASS + if interactive + else Control.MOUSE_FILTER_IGNORE + ) + if _tabs != null: + for child: Node in _tabs.get_children(): + var button := child as Button + if button != null: + button.focus_mode = ( + Control.FOCUS_ALL + if interactive + else Control.FOCUS_NONE + ) + button.mouse_filter = ( + Control.MOUSE_FILTER_STOP + if interactive + else Control.MOUSE_FILTER_IGNORE + ) + _refresh() + + +func focus_initial() -> void: + if _interactive and _tabs != null and _tabs.get_child_count() > 0: + var button := _tabs.get_child(int(_current_view)) as Button + if button != null: + button.grab_focus() + + +func _process(_delta: float) -> void: + if _active and _jobs != null: + _refresh_label.text = _daily_refresh_text() + _refresh_forecast(false) + + +func _build_laptop() -> void: + var margin: MarginContainer = UtilityPageStyle.build_laptop_screen(self) + + var layout := VBoxContainer.new() + layout.add_theme_constant_override("separation", 9) + margin.add_child(layout) + + var title_row := HBoxContainer.new() + title_row.add_theme_constant_override("separation", 16) + layout.add_child(title_row) + var header_left := VBoxContainer.new() + header_left.size_flags_horizontal = Control.SIZE_EXPAND_FILL + header_left.add_theme_constant_override("separation", 0) + title_row.add_child(header_left) + _header = Label.new() + _header.text = "fishnet" + _header.add_theme_font_size_override("font_size", 32) + _header.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + header_left.add_child(_header) + var header_spacer := Control.new() + header_spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL + header_spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE + header_left.add_child(header_spacer) + _tabs = HBoxContainer.new() + _tabs.add_theme_constant_override("separation", 8) + header_left.add_child(_tabs) + for view_index: int in 3: + var tab := Button.new() + tab.text = ["daily jobs", "lifetime jobs", "payments"][view_index] + tab.toggle_mode = true + tab.pressed.connect(_select_view.bind(view_index as View)) + UtilityPageStyle.apply_ocean_button(tab) + _tabs.add_child(tab) + var refresh_alignment_spacer := Control.new() + refresh_alignment_spacer.custom_minimum_size.y = 20.0 + refresh_alignment_spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE + header_left.add_child(refresh_alignment_spacer) + var forecast_column := VBoxContainer.new() + forecast_column.custom_minimum_size.x = 292.0 + forecast_column.size_flags_horizontal = Control.SIZE_SHRINK_END + forecast_column.add_theme_constant_override("separation", 3) + title_row.add_child(forecast_column) + var forecast_panel := PanelContainer.new() + forecast_panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + forecast_panel.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + forecast_column.add_child(forecast_panel) + var forecast_margin := MarginContainer.new() + forecast_margin.add_theme_constant_override("margin_left", 10) + forecast_margin.add_theme_constant_override("margin_top", 8) + forecast_margin.add_theme_constant_override("margin_right", 10) + forecast_margin.add_theme_constant_override("margin_bottom", 8) + forecast_panel.add_child(forecast_margin) + var forecast_stack := VBoxContainer.new() + forecast_stack.add_theme_constant_override("separation", 7) + forecast_margin.add_child(forecast_stack) + var forecast_heading := Label.new() + forecast_heading.text = "weather forecast" + forecast_heading.add_theme_font_size_override("font_size", 20) + forecast_heading.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + forecast_stack.add_child(forecast_heading) + _forecast_list = HBoxContainer.new() + _forecast_list.custom_minimum_size = Vector2(272.0, 66.0) + _forecast_list.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _forecast_list.alignment = BoxContainer.ALIGNMENT_CENTER + _forecast_list.add_theme_constant_override("separation", 6) + forecast_stack.add_child(_forecast_list) + _refresh_label = Label.new() + _refresh_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + _refresh_label.add_theme_font_size_override("font_size", 14) + _refresh_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + forecast_column.add_child(_refresh_label) + + var jobs_column := VBoxContainer.new() + jobs_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL + jobs_column.size_flags_vertical = Control.SIZE_EXPAND_FILL + jobs_column.add_theme_constant_override("separation", 8) + layout.add_child(jobs_column) + + var scroll := ScrollContainer.new() + scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + jobs_column.add_child(scroll) + _list = VBoxContainer.new() + _list.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _list.add_theme_constant_override("separation", 7) + scroll.add_child(_list) + + _status = Label.new() + _status.add_theme_font_size_override("font_size", 16) + _status.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + jobs_column.add_child(_status) + +func _select_view(view: View) -> void: + _current_view = view + _refresh() + focus_initial() + + +func _refresh() -> void: + if _jobs == null or _list == null: + return + _refresh_label.text = _daily_refresh_text() + _refresh_forecast(true) + for index: int in _tabs.get_child_count(): + var tab := _tabs.get_child(index) as Button + if tab != null: + tab.button_pressed = index == int(_current_view) + for child: Node in _list.get_children(): + _list.remove_child(child) + child.queue_free() + match _current_view: + View.DAILY: + _build_job_rows(_jobs.get_daily_jobs(), "no daily jobs available") + View.LIFETIME: + _build_job_rows( + _jobs.get_lifetime_jobs(), "all lifetime jobs complete" + ) + View.PAYMENTS: + _build_payment_rows() + + +func _build_job_rows(jobs: Array[Dictionary], empty_text: String) -> void: + if jobs.is_empty(): + _add_empty(empty_text) + return + for job: Dictionary in jobs: + var row := PanelContainer.new() + row.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + var content := HBoxContainer.new() + content.add_theme_constant_override("separation", 14) + row.add_child(content) + + var text_column := VBoxContainer.new() + text_column.custom_minimum_size.x = 245.0 + text_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL + content.add_child(text_column) + var title := Label.new() + title.text = str(job.get("title", "job")) + title.add_theme_font_size_override("font_size", 20) + title.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + text_column.add_child(title) + var description := Label.new() + description.text = str(job.get("description", "")) + description.add_theme_font_size_override("font_size", 16) + description.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + text_column.add_child(description) + + var target: int = int(job.get("target", 1)) + var progress: int = int(job.get("progress", 0)) + var progress_bar := ProgressBar.new() + progress_bar.custom_minimum_size = Vector2(100.0, 24.0) + progress_bar.max_value = float(target) + progress_bar.value = float(progress) + progress_bar.show_percentage = false + progress_bar.add_theme_stylebox_override( + "background", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_PANEL_DEEP, 9 + ) + ) + progress_bar.add_theme_stylebox_override( + "fill", UtilityPageStyle.rounded_style( + UtilityPageStyle.OCEAN_SELECTED, 9 + ) + ) + content.add_child(progress_bar) + + var count := Label.new() + count.custom_minimum_size.x = 54.0 + count.text = "%d / %d" % [progress, target] + count.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + count.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + count.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + content.add_child(count) + + var reward := _make_reward_display( + int(job.get("fish_coin", 0)), + int(job.get("experience", 0)), + ) + reward.custom_minimum_size.x = 128.0 + content.add_child(reward) + + var claim := Button.new() + claim.custom_minimum_size = Vector2(76.0, 36.0) + var claimable: bool = bool(job.get("claimable", false)) + claim.text = "claim" if claimable else ( + "done" if int(job.get("completed_count", 0)) > 0 else "working" + ) + claim.disabled = not claimable or not _interactive + claim.focus_mode = ( + Control.FOCUS_ALL if not claim.disabled else Control.FOCUS_NONE + ) + claim.mouse_filter = ( + Control.MOUSE_FILTER_STOP + if not claim.disabled + else Control.MOUSE_FILTER_IGNORE + ) + var claim_id: String = str( + job.get("claim_id", job.get("id", "")) + ) + claim.pressed.connect(_claim.bind(claim_id)) + UtilityPageStyle.apply_compact_ocean_button(claim) + content.add_child(claim) + _list.add_child(row) + + +func _build_payment_rows() -> void: + var rewards: Array[Dictionary] = _jobs.get_pending_rewards() + if rewards.is_empty(): + _add_empty("no completed payments waiting") + return + for reward: Dictionary in rewards: + var row := PanelContainer.new() + row.add_theme_stylebox_override( + "panel", UtilityPageStyle.row_style(false) + ) + var content := HBoxContainer.new() + content.add_theme_constant_override("separation", 14) + row.add_child(content) + var reward_details := VBoxContainer.new() + reward_details.size_flags_horizontal = Control.SIZE_EXPAND_FILL + var label := Label.new() + label.text = str(reward.get("title", "completed job")) + label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + reward_details.add_child(label) + reward_details.add_child(_make_reward_display( + int(reward.get("fish_coin", 0)), + int(reward.get("experience", 0)), + )) + content.add_child(reward_details) + var claim := Button.new() + claim.text = "claim" + claim.custom_minimum_size = Vector2(110.0, 42.0) + claim.disabled = not _interactive + claim.focus_mode = ( + Control.FOCUS_ALL if _interactive else Control.FOCUS_NONE + ) + claim.mouse_filter = ( + Control.MOUSE_FILTER_STOP + if _interactive + else Control.MOUSE_FILTER_IGNORE + ) + claim.pressed.connect( + _claim.bind(str(reward.get("claim_id", ""))) + ) + UtilityPageStyle.apply_ocean_button(claim) + content.add_child(claim) + _list.add_child(row) + + +func _claim(claim_id: String) -> void: + if not _jobs.claim(claim_id): + _status.text = "payment could not be completed" + _refresh() + + +func _make_reward_display(fish_coin: int, experience: int) -> HBoxContainer: + var reward := HBoxContainer.new() + reward.mouse_filter = Control.MOUSE_FILTER_IGNORE + reward.add_theme_constant_override("separation", 7) + var currency: CurrencyAmount = ( + CurrencyPresentationType.instantiate_amount(fish_coin, 18.0) + ) + currency.get_amount_label().add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + reward.add_child(currency) + var experience_label := Label.new() + experience_label.text = "• %d xp" % experience + experience_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + experience_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + reward.add_child(experience_label) + return reward + + +func _add_empty(message: String) -> void: + var empty := Label.new() + empty.text = message + empty.custom_minimum_size = Vector2(0.0, 92.0) + empty.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + empty.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + empty.add_theme_font_size_override("font_size", 20) + empty.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _list.add_child(empty) + + +func _refresh_forecast(force: bool) -> void: + if _forecast_list == null: + return + var current_index: int = _current_forecast_index() + if not force and current_index == _forecast_start_index: + return + _forecast_start_index = current_index + for child: Node in _forecast_list.get_children(): + _forecast_list.remove_child(child) + child.queue_free() + if _jobs == null or _world_time == null: + _add_forecast_empty("forecast unavailable") + return + var forecast: Array[Dictionary] = _jobs.get_forecast() + if forecast.is_empty(): + _add_forecast_empty("forecast unavailable") + return + current_index = clampi(current_index, 0, forecast.size() - 1) + var visible_count: int = mini(4, forecast.size()) + for offset: int in visible_count: + var index: int = (current_index + offset) % forecast.size() + var entry: Dictionary = forecast[index] + var start_hour: float = float(entry.get("start_hour", 0.0)) + var weather: WorldWeatherService.Weather = ( + int(entry.get("weather", 0)) as WorldWeatherService.Weather + ) + var slot := Control.new() + slot.custom_minimum_size = Vector2(63.0, 66.0) + slot.mouse_filter = Control.MOUSE_FILTER_IGNORE + _forecast_list.add_child(slot) + var icon := WeatherIcon.new() + icon.position = Vector2(10.5, 17.0) + icon.size = Vector2(42.0, 42.0) + icon.custom_minimum_size = Vector2(42.0, 42.0) + icon.set_weather(weather) + icon.set_nighttime( + WorldTimeService.phase_for_hour(start_hour) + == WorldTimeService.Phase.NIGHT + ) + slot.add_child(icon) + var time_label := Label.new() + time_label.position = Vector2(27.0, 0.0) + time_label.size = Vector2(36.0, 22.0) + time_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + time_label.text = _forecast_exponent_time(start_hour) + time_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + time_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + time_label.add_theme_font_size_override("font_size", 13) + time_label.add_theme_constant_override("outline_size", 3) + time_label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY + ) + time_label.add_theme_color_override( + "font_outline_color", UtilityPageStyle.OCEAN_PANEL_DEEP + ) + slot.add_child(time_label) + + +func _current_forecast_index() -> int: + if _world_time == null: + return -1 + var elapsed: float = fposmod( + _world_time.get_time_hours() - WorldTimeService.DAY_START_HOUR, + WorldTimeService.HOURS_PER_DAY, + ) + return floori(elapsed / JobCatalog.WEATHER_SEGMENT_HOURS) + + +func _compact_clock_time(time_hours: float) -> String: + var normalized: float = fposmod( + time_hours, WorldTimeService.HOURS_PER_DAY + ) + var total_minutes: int = floori(normalized * 60.0) + var hour_24: int = floori(float(total_minutes) / 60.0) + var minute: int = total_minutes % 60 + var hour_12: int = hour_24 % 12 + if hour_12 == 0: + hour_12 = 12 + var suffix: String = "AM" if hour_24 < 12 else "PM" + if minute == 0: + return "%d%s" % [hour_12, suffix] + return "%d:%02d%s" % [hour_12, minute, suffix] + + +func _forecast_exponent_time(time_hours: float) -> String: + return _compact_clock_time(time_hours).to_lower() + + +func _daily_refresh_text() -> String: + if _jobs == null: + return "daily jobs unavailable" + var countdown: String = _jobs.get_time_until_refresh_text() + if countdown.begins_with("refreshes in "): + return "daily jobs refresh in %s" % countdown.trim_prefix( + "refreshes in " + ) + return countdown + + +func _add_forecast_empty(message: String) -> void: + var label := Label.new() + label.text = message + label.custom_minimum_size = Vector2(252.0, 66.0) + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + label.autowrap_mode = TextServer.AUTOWRAP_OFF + label.add_theme_color_override( + "font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY + ) + _forecast_list.add_child(label) + + +func _on_status_changed(message: String) -> void: + _status.text = message diff --git a/ui/the_net_page.gd.uid b/ui/the_net_page.gd.uid new file mode 100644 index 0000000..614db5e --- /dev/null +++ b/ui/the_net_page.gd.uid @@ -0,0 +1 @@ +uid://couoolj844sj3 diff --git a/ui/the_net_page.tscn b/ui/the_net_page.tscn new file mode 100644 index 0000000..db0bb43 --- /dev/null +++ b/ui/the_net_page.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://ui/the_net_page.gd" id="1_net"] + +[node name="TheNetPage" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +script = ExtResource("1_net") diff --git a/ui/title_credits_page.gd b/ui/title_credits_page.gd new file mode 100644 index 0000000..f8841ab --- /dev/null +++ b/ui/title_credits_page.gd @@ -0,0 +1,87 @@ +class_name TitleCreditsPage +extends Control + +signal back_requested(restore_navigation_focus: bool) + +@onready var _paper: PanelContainer = %Paper +@onready var _back_button: Button = %CreditsBackButton + +var _navigation_focus_active: bool = false + + +func _ready() -> void: + UtilityPageStyle.apply_page(self) + _paper.add_theme_stylebox_override( + "panel", UtilityPageStyle.panel_style() + ) + UtilityPageStyle.apply_ocean_button(_back_button) + _back_button.pressed.connect(_request_back) + hide() + + +func open_page(use_navigation_focus: bool) -> void: + _navigation_focus_active = use_navigation_focus + show() + UtilityPageStyle.animate_in(self) + if use_navigation_focus: + call_deferred("_focus_back") + else: + _release_page_focus() + + +func close_page() -> void: + _navigation_focus_active = false + _release_page_focus() + hide() + scale = Vector2.ONE + modulate.a = 1.0 + + +func handle_input(event: InputEvent) -> bool: + if not visible: + return false + if event is InputEventMouseMotion: + _navigation_focus_active = false + _release_page_focus() + return false + if event is InputEventKey and (event as InputEventKey).echo: + return false + var moves_focus: bool = ( + event.is_action_pressed("ui_up") + or event.is_action_pressed("ui_down") + or event.is_action_pressed("ui_left") + or event.is_action_pressed("ui_right") + ) + if moves_focus: + _navigation_focus_active = true + if not _page_has_focus(): + _back_button.grab_focus() + return true + if event.is_action_pressed("ui_cancel"): + _request_back() + return true + return false + + +func get_back_button() -> Button: + return _back_button + + +func _focus_back() -> void: + if visible and _navigation_focus_active: + _back_button.grab_focus() + + +func _request_back() -> void: + back_requested.emit(_navigation_focus_active) + + +func _page_has_focus() -> bool: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + return focus_owner != null and is_ancestor_of(focus_owner) + + +func _release_page_focus() -> void: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner != null and is_ancestor_of(focus_owner): + focus_owner.release_focus() diff --git a/ui/title_credits_page.gd.uid b/ui/title_credits_page.gd.uid new file mode 100644 index 0000000..1cbdf93 --- /dev/null +++ b/ui/title_credits_page.gd.uid @@ -0,0 +1 @@ +uid://c07nehcd5yw6m diff --git a/ui/title_credits_page.tscn b/ui/title_credits_page.tscn new file mode 100644 index 0000000..a667465 --- /dev/null +++ b/ui/title_credits_page.tscn @@ -0,0 +1,180 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/title_credits_page.gd" id="1_script"] +[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] + +[node name="TitleCreditsPage" type="Control"] +visible = false +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +theme = ExtResource("2_theme") +script = ExtResource("1_script") + +[node name="Paper" type="PanelContainer" parent="."] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 160.0 +offset_top = 38.0 +offset_right = 1120.0 +offset_bottom = 682.0 + +[node name="Margin" type="MarginContainer" parent="Paper"] +layout_mode = 2 +theme_override_constants/margin_left = 52 +theme_override_constants/margin_top = 26 +theme_override_constants/margin_right = 52 +theme_override_constants/margin_bottom = 24 + +[node name="Layout" type="VBoxContainer" parent="Paper/Margin"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="Title" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 36 +text = "credits" +horizontal_alignment = 1 + +[node name="Subtitle" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 18 +text = "NETfishing is created by Woofmeow" +horizontal_alignment = 1 + +[node name="Columns" type="HBoxContainer" parent="Paper/Margin/Layout"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 52 + +[node name="CreativeCredits" type="VBoxContainer" parent="Paper/Margin/Layout/Columns"] +custom_minimum_size = Vector2(404, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 5 + +[node name="CreativeHeading" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "creative team" + +[node name="VoyagerName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Voyager" + +[node name="VoyagerRoles" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "co-owner • development • project direction • 3d art • music" +autowrap_mode = 2 + +[node name="EndeavourName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Endeavour" + +[node name="EndeavourRoles" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "co-owner • development • 2d art" +autowrap_mode = 2 + +[node name="ContributorSpacer" type="Control" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +custom_minimum_size = Vector2(0, 9) +layout_mode = 2 + +[node name="ContributorHeading" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "additional contributors" + +[node name="ChillnfillName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "chillnfill" + +[node name="ChillnfillCredit" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "original 3d models for character bodies and arms, ears and tails, and multiple world props and decorative assets including trees and bridges" +autowrap_mode = 2 + +[node name="TekgatorName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Tekgator" + +[node name="TekgatorCredit" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "cooler-capacity, reel-speed, and barrier-power shop icon artwork" +autowrap_mode = 2 + +[node name="AdditionalCredits" type="VBoxContainer" parent="Paper/Margin/Layout/Columns"] +custom_minimum_size = Vector2(404, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 5 + +[node name="AudioHeading" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "additional audio" + +[node name="AudioCredits" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "tosha73 • fishing reel sounds\nkkenny101 • ocean ambience\nqubodup • water impact\nAyton • rain ambience\nivolipa • dog bark\nChristyboy100 • cat meow" + +[node name="TechnologySpacer" type="Control" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +custom_minimum_size = Vector2(0, 9) +layout_mode = 2 + +[node name="TechnologyHeading" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "fonts & technology" + +[node name="TechnologyCredits" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "Tuffy • Thatcher Ulrich, Karoly Barta & Michael Evans\nSeattle Avenue • JLH Fonts\nbuilt with Godot Engine" +autowrap_mode = 2 + +[node name="Notice" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 13 +text = "complete attribution, licensing, and asset provenance are included with every release" +horizontal_alignment = 1 +autowrap_mode = 2 + +[node name="BackCenter" type="CenterContainer" parent="Paper/Margin/Layout"] +layout_mode = 2 + +[node name="CreditsBackButton" type="Button" parent="Paper/Margin/Layout/BackCenter"] +unique_name_in_owner = true +custom_minimum_size = Vector2(112, 50) +layout_mode = 2 +text = "back" diff --git a/ui/title_screen.gd b/ui/title_screen.gd index d950da8..9e17d3e 100644 --- a/ui/title_screen.gd +++ b/ui/title_screen.gd @@ -16,9 +16,13 @@ const BubbleClusterType = preload( const TitleConfirmationBubblePageType = preload( "res://ui/title_confirmation_bubble_page.gd" ) +const TitleCreditsPageType = preload("res://ui/title_credits_page.gd") const NetworkSessionType = preload("res://network/network_session.gd") const SavedServerStoreType = preload("res://network/saved_server_store.gd") const JoinGamePageType = preload("res://ui/network/join_game_page.gd") +const CurrencyPresentationType = preload( + "res://ui/currency_presentation.gd" +) const DECORATIVE_FISH_TEXTURES: Array[Texture2D] = [ preload("res://fish/species/bass/fish_bass_striped.png"), preload("res://fish/species/bluegill/fish_bluegill.png"), @@ -70,7 +74,7 @@ const LOGO_MAX_WIDTH: float = 662.0 const TITLE_HORIZONTAL_MARGIN: float = 48.0 const TITLE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0) const TITLE_COMPACT_REFERENCE_SIZE: Vector2 = Vector2(640.0, 480.0) -const BUBBLE_FIELD_MAX_WIDTH: float = 396.0 +const BUBBLE_FIELD_MAX_WIDTH: float = 440.0 const BUBBLE_FIELD_DESKTOP_HEIGHT: float = 318.0 const BUBBLE_FIELD_COMPACT_WIDTH: float = 294.0 const BUBBLE_FIELD_COMPACT_HEIGHT: float = 200.0 @@ -99,12 +103,13 @@ enum ConfirmationAction { @onready var _continue_button: BubbleButtonType = %ContinueButton @onready var _new_game_button: BubbleButtonType = %NewGameButton @onready var _settings_button: BubbleButtonType = %SettingsButton +@onready var _credits_button: BubbleButtonType = %CreditsButton @onready var _delete_button: BubbleButtonType = %DeleteSaveButton @onready var _quit_button: BubbleButtonType = %QuitButton @onready var _join_game_button: BubbleButtonType = %JoinGameButton @onready var _new_game_label: Label = %NewGameLabel @onready var _delete_save_label: Label = %DeleteSaveLabel -@onready var _feedback_label: Label = %FeedbackLabel +@onready var _feedback_label: RichTextLabel = %FeedbackLabel @onready var _confirmation_page: TitleConfirmationBubblePageType = ( %ConfirmationPage ) @@ -134,6 +139,7 @@ enum ConfirmationAction { @onready var _start_prompt_center: CenterContainer = %StartPromptCenter @onready var _start_prompt_label: Label = %StartPromptLabel @onready var _join_game_page: JoinGamePageType = %JoinGamePage +@onready var _credits_page: TitleCreditsPageType = %CreditsPage var _save_manager: SaveManagerType var _settings_manager: SettingsManagerType @@ -200,6 +206,7 @@ func _ready() -> void: _new_game_button.pressed.connect(_on_new_game_pressed) _join_game_button.pressed.connect(_open_join_game) _settings_button.pressed.connect(_open_settings) + _credits_button.pressed.connect(_open_credits) _delete_button.pressed.connect(_on_delete_pressed) %QuitButton.pressed.connect(_on_quit_pressed) _confirmation_page.confirmed.connect(_on_confirmation_accepted) @@ -211,6 +218,7 @@ func _ready() -> void: _settings_panel.navigation_transition_started.connect( _emit_navigation_bubble_flurry ) + _credits_page.back_requested.connect(_close_credits) _bubble_field.configure(_get_title_buttons()) _decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout) _decorative_bubble_event_timer.timeout.connect( @@ -236,10 +244,13 @@ func setup( network_session: NetworkSessionType, saved_servers: SavedServerStoreType, server_trust: ServerTrustStore, + discovery: DiscoveryClient, ) -> void: _save_manager = save_manager _settings_manager = settings_manager - _join_game_page.setup(network_session, saved_servers, false, server_trust) + _join_game_page.setup( + network_session, saved_servers, false, server_trust, discovery + ) _join_game_page.join_requested.connect(join_game_requested.emit) _join_game_page.back_requested.connect(_close_join_game) Input.mouse_mode = Input.MOUSE_MODE_VISIBLE @@ -258,12 +269,26 @@ func reopen() -> void: _reset_confirmation() _settings_panel.hide() _join_game_page.close_page() + _credits_page.close_page() _refresh_save_inspection() show() _start_decorative_presentation() _start_entry_prompt_animation() +func reopen_to_menu() -> void: + reopen() + _awaiting_start_input = false + _stop_entry_prompt_animation() + _start_prompt_center.hide() + _presentation_center.show() + _button_center.show() + var bubble_field := get_node_or_null("%BubbleField") as Control + if bubble_field != null: + bubble_field.show() + _set_title_bubbles_interactive(true) + + func open_join_game_page(endpoint: String = "") -> void: _cancel_title_entry_transition() _awaiting_start_input = false @@ -271,19 +296,24 @@ func open_join_game_page(endpoint: String = "") -> void: _presentation_center.hide() _settings_panel.hide() _confirmation_page.hide_page() + _credits_page.close_page() _join_game_page.open_page(endpoint) func report_network_error(message: String) -> void: _join_game_page.set_status(message) if not _join_game_page.visible: - _feedback_label.text = message + _feedback_label.text = _center_feedback_text(message) _feedback_label.show() _feedback_label.modulate.a = 1.0 func _open_join_game() -> void: - if _action_in_progress or _is_confirmation_active(): + if ( + _action_in_progress + or _is_confirmation_active() + or _credits_page.visible + ): return open_join_game_page() @@ -296,6 +326,39 @@ func _close_join_game() -> void: _focus_initial_button() +func _open_credits() -> void: + if ( + _action_in_progress + or _is_confirmation_active() + or _settings_panel.visible + or _join_game_page.visible + or _credits_page.visible + ): + return + _hide_continue_stats_context() + var restore_navigation_focus: bool = _navigation_focus_active + _set_title_bubbles_interactive(false) + _release_primary_menu_focus() + _presentation_center.hide() + _start_prompt_center.hide() + _credits_page.open_page(restore_navigation_focus) + + +func _close_credits(restore_navigation_focus: bool) -> void: + _credits_page.close_page() + _presentation_center.show() + _button_center.show() + _start_prompt_center.hide() + _set_title_bubbles_interactive(true) + if restore_navigation_focus: + _navigation_focus_active = true + _credits_button.grab_focus() + else: + _navigation_focus_active = false + _release_title_focus() + _update_continue_stats_visibility() + + func is_awaiting_start_input() -> bool: return _awaiting_start_input @@ -400,6 +463,7 @@ func _get_title_buttons() -> Array[BubbleButton]: _new_game_button, _join_game_button, _settings_button, + _credits_button, _delete_button, _quit_button, ] @@ -490,6 +554,15 @@ func _process(delta: float) -> void: func _input(event: InputEvent) -> void: if not visible: return + if ( + _settings_panel.visible + and _settings_panel.is_controller_mapping_capturing() + ): + return + if _credits_page.visible: + if _credits_page.handle_input(event): + get_viewport().set_input_as_handled() + return if _join_game_page.visible: if event.is_action_pressed("ui_cancel"): _close_join_game() @@ -541,6 +614,7 @@ func _handle_primary_menu_focus_input(event: InputEvent) -> bool: not _button_center.visible or _is_confirmation_active() or _settings_panel.visible + or _credits_page.visible ): return false if event is InputEventMouseMotion: @@ -603,6 +677,10 @@ func _prepare_awaiting_start_input() -> void: _navigation_focus_active = false _modal_restore_navigation_focus = false _button_center.show() + var bubble_field := get_node_or_null("%BubbleField") as Control + if bubble_field != null: + bubble_field.hide() + call_deferred("set_process", visible) _feedback_label.show() _feedback_label.modulate.a = 0.0 _continue_stats_hovered = false @@ -645,6 +723,10 @@ func _reveal_primary_menu() -> void: ): return _awaiting_start_input = false + _button_center.show() + var bubble_field := get_node_or_null("%BubbleField") as Control + if bubble_field != null: + bubble_field.show() _stop_entry_prompt_animation() _title_entry_generation += 1 _title_entry_transition_active = true @@ -887,16 +969,23 @@ func _get_continue_stats_text() -> String: or _inspection.status != SaveInspectionType.Status.VALID_SUPPORTED ): return "" - return ( - "%d fish • $%d • %d discovered" - % [ + return _center_feedback_text( + "%d fish • %s • %d discovered" % [ _inspection.catch_count, - _inspection.wallet_balance, + CurrencyPresentationType.bbcode_amount( + _inspection.wallet_balance, 18 + ), _inspection.discovered_species_count, ] ) +func _center_feedback_text(message: String) -> String: + if message.begins_with("[center]"): + return message + return "[center]%s[/center]" % message + + func _on_continue_pressed() -> void: if ( _action_in_progress @@ -925,13 +1014,15 @@ func _on_new_game_pressed() -> void: new_game_requested.emit() return if _inspection.status == SaveInspectionType.Status.UNSUPPORTED_VERSION: - _feedback_label.text = ( + _feedback_label.text = _center_feedback_text( "this save is from a newer game version. " + "use delete save before starting over." ) return if _inspection.status == SaveInspectionType.Status.IO_ERROR: - _feedback_label.text = "the existing save cannot be accessed safely." + _feedback_label.text = _center_feedback_text( + "the existing save cannot be accessed safely." + ) return _open_confirmation( ConfirmationAction.NEW_GAME, @@ -977,14 +1068,16 @@ func _on_confirmation_accepted() -> void: _action_in_progress = false return if not _save_manager.delete_progression_save(): - _feedback_label.text = "failed to delete saved progression." + _feedback_label.text = _center_feedback_text( + "failed to delete saved progression." + ) _action_in_progress = false _refresh_save_inspection() _begin_confirmation_return() return _save_manager.initialize_new_game() _refresh_save_inspection() - _feedback_label.text = "saved progression deleted." + _feedback_label.text = _center_feedback_text("saved progression deleted.") _begin_confirmation_return() _action_in_progress = false @@ -1170,6 +1263,7 @@ func _open_settings() -> void: _is_confirmation_active() or _action_in_progress or _settings_panel.visible + or _credits_page.visible or _title_settings_transition_active ): return @@ -1258,8 +1352,9 @@ func _begin_title_cluster_return(feedback_text: String) -> void: _title_settings_transition_generation += 1 _cancel_title_settings_tween() _title_settings_transition_active = true - if _feedback_label.text != feedback_text: - _feedback_label.text = feedback_text + var centered_feedback: String = _center_feedback_text(feedback_text) + if _feedback_label.text != centered_feedback: + _feedback_label.text = centered_feedback _feedback_label.visible = _feedback_visible_before_settings _hide_continue_stats_context() _set_title_bubbles_interactive(false) @@ -1367,7 +1462,7 @@ func _refresh_save_inspection() -> void: _delete_save_label.modulate.a = ( DISABLED_BUBBLE_LABEL_ALPHA if _delete_button.disabled else 1.0 ) - _feedback_label.text = _inspection.message + _feedback_label.text = _center_feedback_text(_inspection.message) if _inspection.status == SaveInspectionType.Status.VALID_SUPPORTED: _feedback_label.text = _get_continue_stats_text() if _continue_button.disabled: @@ -1382,6 +1477,7 @@ func _focus_initial_button() -> void: or not visible or _awaiting_start_input or not _button_center.visible + or _credits_page.visible ): return if not _continue_button.disabled: @@ -1397,6 +1493,7 @@ func _on_quit_pressed() -> void: not _action_in_progress and not _is_confirmation_active() and not _settings_panel.visible + and not _credits_page.visible ): _hide_continue_stats_context() quit_requested.emit() @@ -1414,6 +1511,7 @@ func _on_title_visibility_changed() -> void: _navigation_focus_active = false _modal_restore_navigation_focus = false _reset_confirmation() + _credits_page.close_page() _stop_decorative_presentation() diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index 9b8c4a5..ae0c0c6 100644 --- a/ui/title_screen.tscn +++ b/ui/title_screen.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=14 format=3] +[gd_scene load_steps=19 format=3] [ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] @@ -11,6 +11,10 @@ [ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="9_bubble_profile"] [ext_resource type="PackedScene" path="res://ui/title_confirmation_bubble_page.tscn" id="10_confirmation_page"] [ext_resource type="PackedScene" path="res://ui/network/join_game_page.tscn" id="11_join_page"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="12_online_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/settings_dark.png" id="13_settings_dark"] +[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="14_x_dark"] +[ext_resource type="PackedScene" path="res://ui/title_credits_page.tscn" id="15_credits_page"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"] shader = ExtResource("4_water_shader") @@ -18,6 +22,17 @@ shader = ExtResource("4_water_shader") [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_logo"] shader = ExtResource("6_logo_shader") +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_continue_stats"] +content_margin_left = 14.0 +content_margin_top = 6.0 +content_margin_right = 14.0 +content_margin_bottom = 6.0 +bg_color = Color(0.031, 0.122, 0.169, 1) +corner_radius_top_left = 14 +corner_radius_top_right = 14 +corner_radius_bottom_right = 14 +corner_radius_bottom_left = 14 + [node name="TitleScreen" type="Control"] unique_name_in_owner = true z_index = 200 @@ -206,7 +221,7 @@ unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) theme_override_font_sizes/font_size = 22 -text = "v0.3.5-prealpha" +text = "v0.7.1-alpha" horizontal_alignment = 1 [node name="Spacer" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"] @@ -221,21 +236,21 @@ layout_mode = 2 [node name="BubbleLayoutSlot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter"] unique_name_in_owner = true -custom_minimum_size = Vector2(396, 318) +custom_minimum_size = Vector2(440, 318) layout_mode = 2 [node name="BubbleMotionRoot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot"] unique_name_in_owner = true layout_mode = 0 -offset_right = 396.0 +offset_right = 440.0 offset_bottom = 318.0 mouse_filter = 2 [node name="BubbleField" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot"] unique_name_in_owner = true -custom_minimum_size = Vector2(396, 318) +custom_minimum_size = Vector2(440, 318) layout_mode = 0 -offset_right = 396.0 +offset_right = 440.0 offset_bottom = 318.0 script = ExtResource("8_bubble_cluster") profile = ExtResource("9_bubble_profile") @@ -303,7 +318,10 @@ offset_left = 264.0 offset_top = 17.0 offset_right = 384.0 offset_bottom = 133.0 -text = "settings" +texture_filter = 1 +icon = ExtResource("13_settings_dark") +tooltip_text = "settings" +accessibility_name = "settings" script = ExtResource("7_bubble_button") profile = ExtResource("9_bubble_profile") desktop_anchor = Vector2(324, 75) @@ -316,13 +334,40 @@ motion_period = 4.9 motion_phase = 2.5 deformation_period = 6.3 +[node name="CreditsButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"] +unique_name_in_owner = true +offset_left = 330.0 +offset_top = 125.0 +offset_right = 414.0 +offset_bottom = 207.0 +text = "credits" +tooltip_text = "credits" +accessibility_name = "credits" +script = ExtResource("7_bubble_button") +profile = ExtResource("9_bubble_profile") +neutral_size = Vector2(84, 82) +desktop_anchor = Vector2(372, 166) +compact_anchor = Vector2(372, 166) +compact_minimum_size = Vector2(62, 62) +minimum_font_size = 12 +maximum_font_size = 17 +horizontal_amplitude = 1.4 +vertical_amplitude = 3.0 +motion_period = 5.0 +motion_phase = 1.8 +deformation_amplitude = 0.015 +deformation_period = 5.6 + [node name="JoinGameButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"] unique_name_in_owner = true offset_left = 126.0 offset_top = 224.0 offset_right = 248.0 offset_bottom = 342.0 -text = "join\ngame" +texture_filter = 1 +icon = ExtResource("12_online_dark") +tooltip_text = "join game" +accessibility_name = "join game" script = ExtResource("7_bubble_button") profile = ExtResource("9_bubble_profile") neutral_size = Vector2(122, 118) @@ -380,7 +425,10 @@ offset_left = 275.0 offset_top = 217.0 offset_right = 369.0 offset_bottom = 309.0 -text = "quit" +texture_filter = 1 +icon = ExtResource("14_x_dark") +tooltip_text = "quit" +accessibility_name = "quit" script = ExtResource("7_bubble_button") profile = ExtResource("9_bubble_profile") neutral_size = Vector2(94, 92) @@ -396,15 +444,18 @@ motion_phase = 4.8 deformation_amplitude = 0.02 deformation_period = 4.8 -[node name="FeedbackLabel" type="Label" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"] +[node name="FeedbackLabel" type="RichTextLabel" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"] unique_name_in_owner = true visible = false -custom_minimum_size = Vector2(0, 44) +custom_minimum_size = Vector2(430, 44) layout_mode = 2 +size_flags_horizontal = 4 text = "" -horizontal_alignment = 1 -vertical_alignment = 1 +bbcode_enabled = true +scroll_active = false autowrap_mode = 2 +vertical_alignment = 1 +theme_override_styles/normal = SubResource("StyleBoxFlat_continue_stats") [node name="ConfirmationPage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("10_confirmation_page")] unique_name_in_owner = true @@ -432,6 +483,16 @@ offset_bottom = 300.0 grow_horizontal = 2 grow_vertical = 2 +[node name="CreditsPage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("15_credits_page")] +unique_name_in_owner = true +z_index = 220 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="JoinGamePage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("11_join_page")] unique_name_in_owner = true z_index = 220 diff --git a/ui/typewriter_reveal.gd b/ui/typewriter_reveal.gd new file mode 100644 index 0000000..aa4f3d6 --- /dev/null +++ b/ui/typewriter_reveal.gd @@ -0,0 +1,47 @@ +class_name TypewriterReveal +extends RefCounted + +const DEFAULT_CHARACTERS_PER_SECOND: float = 28.0 +const MIN_CHARACTERS_PER_SECOND: float = 12.0 +const MAX_CHARACTERS_PER_SECOND: float = 60.0 + +static var _characters_per_second: float = DEFAULT_CHARACTERS_PER_SECOND + + +static func set_characters_per_second(value: float) -> void: + _characters_per_second = clampf( + value, + MIN_CHARACTERS_PER_SECOND, + MAX_CHARACTERS_PER_SECOND, + ) + + +static func get_characters_per_second() -> float: + return _characters_per_second + + +static func start( + label: Label, + characters_per_second: float = -1.0, +) -> float: + if label == null: + return 0.0 + var character_count: int = label.get_total_character_count() + if character_count <= 0: + label.visible_characters = -1 + return 0.0 + label.visible_characters = 0 + var resolved_characters_per_second := ( + characters_per_second + if characters_per_second > 0.0 + else _characters_per_second + ) + var duration := float(character_count) / resolved_characters_per_second + var reveal_tween := label.create_tween() + reveal_tween.tween_property( + label, + "visible_characters", + character_count, + duration, + ).from(0).set_trans(Tween.TRANS_LINEAR) + return duration diff --git a/ui/typewriter_reveal.gd.uid b/ui/typewriter_reveal.gd.uid new file mode 100644 index 0000000..2d96fd3 --- /dev/null +++ b/ui/typewriter_reveal.gd.uid @@ -0,0 +1 @@ +uid://c1as5qlog6ijy diff --git a/ui/ui_pixelation_presenter.gd b/ui/ui_pixelation_presenter.gd index 18481c3..2cc2063 100644 --- a/ui/ui_pixelation_presenter.gd +++ b/ui/ui_pixelation_presenter.gd @@ -5,6 +5,13 @@ const MIN_UI_VIEWPORT_SIZE: Vector2i = Vector2i(256, 180) const UIReferencePresentationType = preload( "res://ui/ui_reference_presentation.gd" ) +const ControllerFocusPresentationType = preload( + "res://ui/controller_focus_presentation.gd" +) +const ControllerFocusRecoveryType = preload( + "res://ui/controller_focus_recovery.gd" +) +const OnScreenKeyboardType = preload("res://ui/on_screen_keyboard.gd") signal effective_pixel_size_changed( requested_pixel_size: int, @@ -16,6 +23,9 @@ signal effective_pixel_size_changed( @onready var _canonical_stage: Control = ( $UIViewport/GameUI/UIRoot/CanonicalStage ) +@onready var _hotbar: HotbarUI = ( + $UIViewport/GameUI/UIRoot/CanonicalStage/Hotbar +) @onready var _chat_ui: ChatUI = $UIViewport/GameUI/UIRoot/ChatUI @onready var _title_content_stage: Control = ( $UIViewport/GameUI/UIRoot/TitleScreen/ResponsiveTitleStage @@ -26,14 +36,25 @@ var _effective_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE var _gameplay_active: bool = false var _interactive_ui_open: bool = false var _passive_pointer_ui_enabled: bool = false +var _on_screen_keyboard: OnScreenKeyboardType func _ready() -> void: + _on_screen_keyboard = OnScreenKeyboardType.new() + _ui_root.add_child(_on_screen_keyboard) + var controller_focus_recovery := ControllerFocusRecoveryType.new() + _ui_root.add_child(controller_focus_recovery) + var controller_focus_presentation := ControllerFocusPresentationType.new() + _ui_root.add_child(controller_focus_presentation) var root_viewport: Viewport = get_viewport() root_viewport.size_changed.connect(_resize_presentation) _resize_presentation() +func set_on_screen_keyboard_enabled(enabled: bool) -> void: + _on_screen_keyboard.set_enabled(enabled) + + func set_pixel_size(pixel_size: int) -> void: _requested_pixel_size = clampi( pixel_size, @@ -120,6 +141,7 @@ func _resize_presentation() -> void: UIReferencePresentationType.get_stage_position(display_size) ) _canonical_stage.size = UIReferencePresentationType.REFERENCE_SIZE + _hotbar.position = Vector2(0.0, _canonical_stage.position.y) _title_content_stage.set_anchors_preset(Control.PRESET_TOP_LEFT) _title_content_stage.position = _canonical_stage.position _title_content_stage.size = UIReferencePresentationType.REFERENCE_SIZE diff --git a/ui/utility_page_style.gd b/ui/utility_page_style.gd index a0b40be..9045cbc 100644 --- a/ui/utility_page_style.gd +++ b/ui/utility_page_style.gd @@ -26,12 +26,65 @@ const GREEN: Color = Color("31594d") const LIGHT_TEXT: Color = Color("f5eed9") const DISABLED_TEXT: Color = Color(0.33, 0.36, 0.35, 0.72) const MOTION_TWEEN_META: StringName = &"utility_page_motion_tween" +const LAPTOP_RECT: Rect2 = Rect2(66.0, 132.0, 1148.0, 520.0) static func apply_page(root: Control) -> void: root.add_theme_font_override("font", TuffyFont) +static func rounded_style(color: Color, radius: int) -> StyleBoxFlat: + var style := StyleBoxFlat.new() + style.bg_color = color + style.set_border_width_all(0) + style.set_corner_radius_all(radius) + return style + + +static func build_laptop_screen( + root: Control, + laptop_rect: Rect2 = LAPTOP_RECT, +) -> MarginContainer: + var laptop := PanelContainer.new() + laptop.position = laptop_rect.position + laptop.size = laptop_rect.size + laptop.add_theme_stylebox_override( + "panel", rounded_style(OCEAN_PANEL_MID, 24) + ) + root.add_child(laptop) + + var shell_margin := MarginContainer.new() + shell_margin.add_theme_constant_override("margin_left", 20) + shell_margin.add_theme_constant_override("margin_top", 18) + shell_margin.add_theme_constant_override("margin_right", 20) + shell_margin.add_theme_constant_override("margin_bottom", 24) + laptop.add_child(shell_margin) + + var screen := PanelContainer.new() + screen.add_theme_stylebox_override( + "panel", rounded_style(OCEAN_FIELD, 16) + ) + shell_margin.add_child(screen) + + var content_margin := MarginContainer.new() + content_margin.add_theme_constant_override("margin_left", 24) + content_margin.add_theme_constant_override("margin_top", 18) + content_margin.add_theme_constant_override("margin_right", 24) + content_margin.add_theme_constant_override("margin_bottom", 18) + screen.add_child(content_margin) + + var base := ColorRect.new() + base.position = Vector2( + laptop_rect.position.x + 82.0, + laptop_rect.end.y - 6.0, + ) + base.size = Vector2(laptop_rect.size.x - 164.0, 14.0) + base.color = OCEAN_PANEL_MID + base.mouse_filter = Control.MOUSE_FILTER_IGNORE + root.add_child(base) + return content_margin + + static func panel_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = OCEAN_PANEL_DEEP @@ -120,6 +173,22 @@ static func apply_ocean_button(button: BaseButton) -> void: button.custom_minimum_size.y = maxf(button.custom_minimum_size.y, 40.0) +static func apply_compact_ocean_button(button: BaseButton) -> void: + apply_ocean_button(button) + for state: StringName in [ + &"normal", &"hover", &"pressed", &"focus", &"disabled", + ]: + var style := button.get_theme_stylebox(state).duplicate() as StyleBoxFlat + if style == null: + continue + style.content_margin_left = 10.0 + style.content_margin_right = 10.0 + style.content_margin_top = 4.0 + style.content_margin_bottom = 4.0 + button.add_theme_stylebox_override(state, style) + button.custom_minimum_size.y = 34.0 + + static func apply_line_edit(edit: LineEdit) -> void: edit.add_theme_font_override("font", TuffyFont) edit.add_theme_color_override("font_color", LIGHT_TEXT) diff --git a/ui/weather_icon.gd b/ui/weather_icon.gd index 894e140..b44bc9b 100644 --- a/ui/weather_icon.gd +++ b/ui/weather_icon.gd @@ -1,11 +1,21 @@ class_name WeatherIcon extends Control -const BUBBLE_COLOR := Color(0.025, 0.13, 0.19, 0.94) -const ICON_COLOR := Color(0.78, 0.91, 0.95) -const SUN_COLOR := Color(0.98, 0.82, 0.34) -const MOON_COLOR := Color(0.78, 0.88, 1.0) -const RAIN_COLOR := Color(0.28, 0.73, 0.82) +const CLEAR_DAY_TEXTURE: Texture2D = preload( + "res://ui/icons/weather/weather_clear_day.png" +) +const CLEAR_NIGHT_TEXTURE: Texture2D = preload( + "res://ui/icons/weather/weather_clear_night_full.png" +) +const CLOUDY_TEXTURE: Texture2D = preload( + "res://ui/icons/weather/weather_cloudy.png" +) +const FOG_TEXTURE: Texture2D = preload( + "res://ui/icons/weather/weather_fog.png" +) +const RAIN_TEXTURE: Texture2D = preload( + "res://ui/icons/weather/weather_rain.png" +) var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY var _is_nighttime: bool = false @@ -14,7 +24,8 @@ var _is_nighttime: bool = false func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_PASS focus_mode = Control.FOCUS_NONE - custom_minimum_size = Vector2(34.0, 34.0) + texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + custom_minimum_size = Vector2(51.0, 51.0) _update_tooltip() queue_redraw() @@ -28,10 +39,10 @@ func set_weather(weather: WorldWeatherService.Weather) -> void: queue_redraw() -func set_nighttime(is_nighttime: bool) -> void: - if _is_nighttime == is_nighttime: +func set_nighttime(nighttime: bool) -> void: + if _is_nighttime == nighttime: return - _is_nighttime = is_nighttime + _is_nighttime = nighttime queue_redraw() @@ -44,55 +55,22 @@ func get_weather() -> WorldWeatherService.Weather: func _draw() -> void: - var center: Vector2 = size * 0.5 - var radius: float = minf(size.x, size.y) * 0.5 - draw_circle(center, radius, BUBBLE_COLOR) + var icon_size := Vector2.ONE * minf(size.x, size.y) + var icon_rect := Rect2((size - icon_size) * 0.5, icon_size) + draw_texture_rect(_get_weather_texture(), icon_rect, false) + + +func _get_weather_texture() -> Texture2D: match _weather: WorldWeatherService.Weather.SUNNY: - if _is_nighttime: - _draw_moon(center) - else: - _draw_sun(center) + return CLEAR_NIGHT_TEXTURE if _is_nighttime else CLEAR_DAY_TEXTURE WorldWeatherService.Weather.CLOUDY: - _draw_cloud(center + Vector2(0.0, 1.0)) + return CLOUDY_TEXTURE WorldWeatherService.Weather.RAINY: - _draw_cloud(center + Vector2(0.0, -2.0)) - for x_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]): - draw_line( - center + Vector2(x_offset + 1.0, 5.0), - center + Vector2(x_offset - 1.0, 9.0), - RAIN_COLOR, - 2.0, - true, - ) + return RAIN_TEXTURE WorldWeatherService.Weather.FOGGY: - for y_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]): - draw_line( - center + Vector2(-9.0, y_offset), - center + Vector2(9.0, y_offset), - ICON_COLOR, - 2.0, - true, - ) - - -func _draw_sun(center: Vector2) -> void: - draw_circle(center, 5.0, SUN_COLOR) - for index: int in 8: - var angle: float = TAU * float(index) / 8.0 - var direction := Vector2(cos(angle), sin(angle)) - draw_line( - center + direction * 8.0, - center + direction * 11.0, - SUN_COLOR, - 2.0, - true, - ) - - -func _draw_moon(center: Vector2) -> void: - draw_circle(center, 8.0, MOON_COLOR) - draw_circle(center + Vector2(4.0, -2.0), 7.0, BUBBLE_COLOR) + return FOG_TEXTURE + return CLEAR_DAY_TEXTURE func _update_tooltip() -> void: @@ -101,10 +79,3 @@ func _update_tooltip() -> void: if _weather == WorldWeatherService.Weather.SUNNY else WorldWeatherService.weather_name(_weather) ) - - -func _draw_cloud(center: Vector2) -> void: - draw_circle(center + Vector2(-5.0, 0.0), 5.0, ICON_COLOR) - draw_circle(center + Vector2(0.0, -3.0), 6.0, ICON_COLOR) - draw_circle(center + Vector2(6.0, 0.0), 5.0, ICON_COLOR) - draw_rect(Rect2(center + Vector2(-9.0, 0.0), Vector2(18.0, 5.0)), ICON_COLOR) diff --git a/world/README-WORLD-AUTHORING.md b/world/README-WORLD-AUTHORING.md deleted file mode 100644 index d596c05..0000000 --- a/world/README-WORLD-AUTHORING.md +++ /dev/null @@ -1,127 +0,0 @@ -# World authoring - -The active test world uses the human-authored starter island through -`world/regions/starter_island_region.tscn`. - -## Active composition - -`world/test_world.tscn` owns the gameplay-wide environment, sun, world bounds, -and below-world failsafe. `StarterIslandRegion` owns the placed island content: - -```text -StarterIslandRegion -├── Terrain -│ ├── Visual -│ └── Collision -├── WaterBodies -│ ├── Pond -│ │ ├── VisualWater -│ │ ├── FishingRegion -│ │ └── RecoveryRegion -│ └── Ocean -│ ├── VisualWater -│ ├── FishingRegions -│ └── RecoveryRegions -├── PlayerSpawn -├── SafeRespawns -└── Interactables - ├── FishingShopWorld - └── PelicanCoolerPerch -``` - -Move the meaningful feature root rather than one of its implementation -children. Child transforms are local offsets owned by that feature. - -## Water bodies - -Every fishable region must author its `water_type` explicitly. The starter pond -is `FRESH_WATER`; the starter coast is `SALT_WATER`. Fish species use the same -central type definition through an allowed-water-type bitmask, so pool contents -and authoritative selection are both validated against the region. Do not infer -habitat from node names, pool filenames, coordinates, or water height. - -Select `WaterBodies/Pond` to move or resize the pond. Its transform is the -authoritative surface position. The `surface_size`, fishing padding, recovery -padding, and recovery depth properties update the visible water and gameplay -coverage together. Fishing and recovery derive the surface height from the -owned surface transform. - -Select `WaterBodies/Ocean` to move the surrounding water as one feature. Its -visual, fishing lobes, and recovery lobes use local transforms beneath the -ocean root. The existing multi-lobe footprint remains intentionally explicit; -changing that footprint still requires editing its owned lobe children. - -## Placed features - -- `PlayerSpawn` is the authoritative initial player transform. Its rotation - controls initial facing. -- Each marker under `SafeRespawns` is an authoritative recovery destination. - Recovery preserves the player's current facing; marker rotation is - intentionally not applied. -- `Interactables/FishingShopWorld` owns the shop visual, collision, - interaction area, prompt, and entrance marker. -- `Interactables/PelicanCoolerPerch` owns the complete Pelican landmark visual. - Pelican selling remains a Cooler action and has no world interaction area. -- `Terrain` owns both the imported visual and generated terrain collision. -- `BelowWorldFailsafe/Coverage` exposes the below-world recovery coverage as a - saved collision shape in `world/test_world.tscn`. - -The active `Environment`, `Sun`, and external starter-island grass material -remain directly editable through the Inspector. Their tuning is not recreated -by runtime scripts. - -## Reusable world props - -Use a single placed `WorldProp` root for trees, rocks, buildings, benches, -signs, vegetation, and similar landmarks: - -```text -WorldProp -├── Visual -└── Collision - └── CollisionShape3D -``` - -Add optional interaction areas, labels, and markers beneath the same root. -Instance imported GLB visuals beneath `Visual`; keep gameplay collision -Godot-owned beneath `Collision`. Place and duplicate the complete prop scene, -not its visual child. - -Author reusable assets at scale `(1, 1, 1)`. Uniform root scaling is acceptable -for provisional props when visual and collision scale together. Avoid -non-uniform root scaling because it can deform collision and imported geometry -unpredictably. Make mutable per-instance shapes, meshes, and materials local to -the scene so editing one prop does not change unrelated instances. - -## Shoreline tide ribbons - -Each map that uses generated tide ribbons owns a `ShorelineRibbonBaker` node and -one `ShorelineRibbonConfig` resource per water body. Each fishable water body -declares the shared `WaterType` value used by fishing, Logbook classification, -and shoreline presentation. A configuration designates -only the static terrain `CollisionShape3D` or `MeshInstance3D` to intersect, the -water height, explicit generation bounds, water-facing reference, optional -smoothing overrides, and the generated `.tres` output path. Props, docks, -players, bobbers, and gameplay areas are not scanned unless a map author -explicitly selects one as the terrain source. The baker reports every configured -body but intentionally generates ribbons only for `SALT_WATER`; freshwater uses -the shared depth-tinted shader without tide marks or an advancing wash. - -To rebuild in the editor, select the map's `ShorelineRibbonBaker` node and press -**Rebuild Shoreline Ribbons** in the Inspector. The equivalent validation/CI -command for the starter map is: - -```sh -godot --headless --path . --script scripts/bake_shoreline_ribbons.gd -``` - -Set the baker's `debug_path_stage` to Raw, Simplified, or Smoothed before an -editor rebuild to compare the extraction stages. Keep it Off in the finished -scene; the temporary editor-only line preview is never saved as gameplay data. - -Rebuild a saltwater ribbon only when its terrain at the waterline, configured -water height, or generation bounds change. Freshwater terrain changes do not -require a ribbon rebuild because freshwater bodies have no ribbon. Adding or -moving unrelated props, fishing areas, recovery volumes, or other gameplay -nodes does not require a rebuild. Normal gameplay loads the committed generated -saltwater meshes and never runs the extraction step. diff --git a/world/environment/local_storm_cloud.gdshader b/world/environment/local_storm_cloud.gdshader new file mode 100644 index 0000000..b5ec03b --- /dev/null +++ b/world/environment/local_storm_cloud.gdshader @@ -0,0 +1,37 @@ +shader_type spatial; +render_mode blend_mix, depth_prepass_alpha, cull_disabled, unshaded, fog_disabled, shadows_disabled; + +uniform vec4 cloud_color : source_color = vec4(0.07, 0.10, 0.12, 1.0); +uniform float weather_opacity : hint_range(0.0, 1.0, 0.01) = 0.0; +uniform float maximum_opacity : hint_range(0.0, 1.0, 0.01) = 0.5; +uniform float distance_fade_start : hint_range(50.0, 500.0, 1.0) = 240.0; +uniform float distance_fade_end : hint_range(50.0, 500.0, 1.0) = 300.0; + +varying float world_normal_height; +varying float camera_distance; + + +void vertex() { + world_normal_height = normalize(MODEL_NORMAL_MATRIX * NORMAL).y; + vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; + camera_distance = distance( + world_position.xz, + CAMERA_POSITION_WORLD.xz + ); +} + + +void fragment() { + float facet_light = mix( + 0.68, + 1.06, + clamp(world_normal_height * 0.5 + 0.5, 0.0, 1.0) + ); + ALBEDO = cloud_color.rgb * facet_light; + float distance_fade = 1.0 - smoothstep( + distance_fade_start, + distance_fade_end, + camera_distance + ); + ALPHA = weather_opacity * maximum_opacity * distance_fade * cloud_color.a; +} diff --git a/world/environment/local_storm_cloud.gdshader.uid b/world/environment/local_storm_cloud.gdshader.uid new file mode 100644 index 0000000..b07fd92 --- /dev/null +++ b/world/environment/local_storm_cloud.gdshader.uid @@ -0,0 +1 @@ +uid://c20wlon7hc48g diff --git a/world/environment/local_storm_cloud_layer.gd b/world/environment/local_storm_cloud_layer.gd new file mode 100644 index 0000000..2ec0008 --- /dev/null +++ b/world/environment/local_storm_cloud_layer.gd @@ -0,0 +1,228 @@ +class_name LocalStormCloudLayer +extends Node3D + +const CLOUD_SHADER: Shader = preload( + "res://world/environment/local_storm_cloud.gdshader" +) +const MIN_CLOUD_ALTITUDE: float = 15.0 +const MAX_CLOUD_ALTITUDE: float = 23.0 +const GRID_AXIS_COUNT: int = 9 +const GRID_SPACING: float = 70.0 +const FIELD_HALF_EXTENT: float = GRID_AXIS_COUNT * GRID_SPACING * 0.5 +const DISTANCE_FADE_START: float = 240.0 +const DISTANCE_FADE_END: float = 300.0 +const WRAP_RADIUS: float = FIELD_HALF_EXTENT +const MAXIMUM_OPACITY: float = 1.0 +const BASE_VELOCITY := Vector2(0.825, -0.45) +const BODY_OUTLINE: Array[Vector2] = [ + Vector2(-6.9, -0.8), + Vector2(-6.6, -2.0), + Vector2(-5.7, -3.0), + Vector2(-4.5, -3.5), + Vector2(-3.0, -3.7), + Vector2(-1.8, -4.2), + Vector2(-0.5, -4.3), + Vector2(0.7, -4.0), + Vector2(2.0, -4.1), + Vector2(3.0, -3.5), + Vector2(4.6, -3.3), + Vector2(5.8, -2.5), + Vector2(6.6, -1.5), + Vector2(7.0, -0.2), + Vector2(6.8, 1.0), + Vector2(6.1, 1.9), + Vector2(6.0, 2.9), + Vector2(5.1, 3.7), + Vector2(3.8, 4.0), + Vector2(2.7, 4.7), + Vector2(1.4, 4.9), + Vector2(0.2, 5.3), + Vector2(-1.2, 5.0), + Vector2(-2.1, 4.4), + Vector2(-3.6, 4.2), + Vector2(-4.9, 3.5), + Vector2(-5.4, 2.5), + Vector2(-6.3, 1.7), + Vector2(-6.8, 0.6), +] + +var _target: Node3D +var _material: ShaderMaterial +var _multimesh: MultiMesh +var _cloud_mesh: MultiMeshInstance3D +var _drift_offset := Vector2.ZERO +var _storm_amount: float = 0.0 + + +func setup(target: Node3D) -> void: + _target = target + _build_cloud_field() + _update_cloud_transforms() + set_process(true) + + +func set_storm_amount(amount: float, color: Color) -> void: + _storm_amount = clampf(amount, 0.0, 1.0) + var should_be_visible: bool = _storm_amount > 0.001 + if should_be_visible and not visible: + _update_cloud_transforms() + visible = should_be_visible + if _material == null: + return + _material.set_shader_parameter("weather_opacity", _storm_amount) + _material.set_shader_parameter("cloud_color", color) + + +func get_storm_amount() -> float: + return _storm_amount + + +func get_patch_count() -> int: + return GRID_AXIS_COUNT * GRID_AXIS_COUNT + + +func _process(delta: float) -> void: + if not visible or _target == null or not is_instance_valid(_target): + return + _drift_offset += BASE_VELOCITY * delta + _drift_offset = Vector2( + _wrap_axis(_drift_offset.x), + _wrap_axis(_drift_offset.y), + ) + _update_cloud_transforms() + + +func _build_cloud_field() -> void: + _material = ShaderMaterial.new() + _material.shader = CLOUD_SHADER + _material.set_shader_parameter("weather_opacity", 0.0) + _material.set_shader_parameter("maximum_opacity", MAXIMUM_OPACITY) + _material.set_shader_parameter("distance_fade_start", DISTANCE_FADE_START) + _material.set_shader_parameter("distance_fade_end", DISTANCE_FADE_END) + + var cloud_body_mesh: ArrayMesh = _build_cloud_body_mesh() + cloud_body_mesh.surface_set_material(0, _material) + + _multimesh = MultiMesh.new() + _multimesh.transform_format = MultiMesh.TRANSFORM_3D + _multimesh.mesh = cloud_body_mesh + _multimesh.instance_count = get_patch_count() + _multimesh.custom_aabb = AABB( + Vector3( + -FIELD_HALF_EXTENT - 60.0, + MIN_CLOUD_ALTITUDE - 5.0, + -FIELD_HALF_EXTENT - 60.0, + ), + Vector3( + (FIELD_HALF_EXTENT + 60.0) * 2.0, + MAX_CLOUD_ALTITUDE - MIN_CLOUD_ALTITUDE + 10.0, + (FIELD_HALF_EXTENT + 60.0) * 2.0, + ), + ) + + _cloud_mesh = MultiMeshInstance3D.new() + _cloud_mesh.name = "CloudField" + _cloud_mesh.multimesh = _multimesh + _cloud_mesh.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF + add_child(_cloud_mesh) + visible = false + + +func _build_cloud_body_mesh() -> ArrayMesh: + var outline := PackedVector2Array(BODY_OUTLINE) + var surface_tool := SurfaceTool.new() + surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES) + var face_indices: PackedInt32Array = Geometry2D.triangulate_polygon( + outline + ) + for triangle_offset: int in range(0, face_indices.size(), 3): + var point_a: Vector2 = outline[face_indices[triangle_offset]] + var point_b: Vector2 = outline[face_indices[triangle_offset + 1]] + var point_c: Vector2 = outline[face_indices[triangle_offset + 2]] + _add_oriented_triangle( + surface_tool, + _to_cloud_vertex(point_a), + _to_cloud_vertex(point_b), + _to_cloud_vertex(point_c), + Vector3.DOWN, + ) + surface_tool.index() + return surface_tool.commit() + + +func _to_cloud_vertex(point: Vector2) -> Vector3: + return Vector3(point.x, 0.0, point.y) + + +func _add_oriented_triangle( + surface_tool: SurfaceTool, + point_a: Vector3, + point_b: Vector3, + point_c: Vector3, + expected_normal: Vector3, +) -> void: + if (point_b - point_a).cross(point_c - point_a).dot(expected_normal) < 0.0: + var swap: Vector3 = point_b + point_b = point_c + point_c = swap + for point: Vector3 in [point_a, point_b, point_c]: + surface_tool.set_normal(expected_normal) + surface_tool.add_vertex(point) + + +func _update_cloud_transforms() -> void: + if _target == null or _multimesh == null: + return + global_position = _target.global_position + var target_xz := Vector2( + _target.global_position.x, + _target.global_position.z, + ) + var grid_center: float = float(GRID_AXIS_COUNT - 1) * 0.5 + var instance_index: int = 0 + for grid_z: int in GRID_AXIS_COUNT: + for grid_x: int in GRID_AXIS_COUNT: + var patch_index: int = grid_z * GRID_AXIS_COUNT + grid_x + var world_offset := Vector2( + (float(grid_x) - grid_center) * GRID_SPACING, + (float(grid_z) - grid_center) * GRID_SPACING, + ) + _drift_offset + var relative := Vector2( + _wrap_axis(world_offset.x - target_xz.x), + _wrap_axis(world_offset.y - target_xz.y), + ) + var altitude: float = lerpf( + MIN_CLOUD_ALTITUDE, + MAX_CLOUD_ALTITUDE, + float(patch_index % 4) / 3.0, + ) + var patch_scale: float = 2.20 + float( + (patch_index * 7) % 10 + ) * 0.10 + var width_variation: float = 0.90 + float( + (patch_index * 5) % 7 + ) * 0.04 + var depth_variation: float = 0.90 + float( + (patch_index * 3 + 2) % 7 + ) * 0.04 + var patch_basis := Basis( + Vector3.UP, + deg_to_rad(float(patch_index * 17 % 41) - 20.0), + ).scaled(Vector3( + patch_scale * 2.35 * width_variation, + patch_scale * 0.34, + patch_scale * 2.65 * depth_variation, + )) + _multimesh.set_instance_transform( + instance_index, + Transform3D( + patch_basis, + Vector3(relative.x, altitude, relative.y), + ), + ) + instance_index += 1 + + +func _wrap_axis(value: float) -> float: + return fposmod(value + FIELD_HALF_EXTENT, FIELD_HALF_EXTENT * 2.0) \ + - FIELD_HALF_EXTENT diff --git a/world/environment/local_storm_cloud_layer.gd.uid b/world/environment/local_storm_cloud_layer.gd.uid new file mode 100644 index 0000000..d274b64 --- /dev/null +++ b/world/environment/local_storm_cloud_layer.gd.uid @@ -0,0 +1 @@ +uid://krinusur24lh diff --git a/world/environment/netfishing_environment.tres b/world/environment/netfishing_environment.tres index 747d3ce..4f625e4 100644 --- a/world/environment/netfishing_environment.tres +++ b/world/environment/netfishing_environment.tres @@ -10,7 +10,7 @@ ambient_light_source = 2 ambient_light_color = Color(0.82, 0.9, 0.92, 1) ambient_light_energy = 1.08 reflected_light_source = 2 -fog_enabled = true +fog_enabled = false fog_mode = 1 fog_light_color = Color(0.54902, 0.74902, 0.764706, 1) fog_light_energy = 0.82 diff --git a/world/environment/netfishing_sky.gdshader b/world/environment/netfishing_sky.gdshader index b70ab79..07d3c53 100644 --- a/world/environment/netfishing_sky.gdshader +++ b/world/environment/netfishing_sky.gdshader @@ -1,9 +1,14 @@ shader_type sky; +#include "res://world/environment/netfishing_sky_clouds.gdshaderinc" + uniform vec4 sky_top_color : source_color = vec4(0.204, 0.498, 0.643, 1.0); uniform vec4 sky_horizon_color : source_color = vec4(0.663, 0.843, 0.847, 1.0); uniform vec4 ground_bottom_color : source_color = vec4(0.157, 0.361, 0.439, 1.0); uniform vec4 ground_horizon_color : source_color = vec4(0.549, 0.749, 0.765, 1.0); +uniform float horizon_blend_width : hint_range(0.005, 0.15, 0.005) = 0.01; +uniform vec4 fog_horizon_color : source_color = vec4(0.13, 0.21, 0.32, 1.0); +uniform float fog_horizon_occlusion : hint_range(0.0, 1.0) = 0.0; uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0); uniform vec4 sun_color : source_color = vec4(1.0, 0.86, 0.46, 1.0); @@ -39,7 +44,11 @@ void sky() { vec3 base_color = mix( ground_color, sky_color, - smoothstep(-0.01, 0.01, view_direction.y) + smoothstep( + -horizon_blend_width, + horizon_blend_width, + view_direction.y + ) ); float alignment = dot(view_direction, normalize(sun_direction)); @@ -78,6 +87,15 @@ void sky() { base_color = mix( base_color, moon_color.rgb, moon_disc * moon_visible_strength ); + base_color = apply_weather_clouds(base_color, view_direction); + // Compatibility fog does not fully erase the color split between a custom + // sky's upper and lower hemispheres. Dense low-light fog supplies one + // uniform sky color here so that split cannot masquerade as an ocean edge. + base_color = mix( + base_color, + fog_horizon_color.rgb, + fog_horizon_occlusion + ); COLOR = base_color; } diff --git a/world/environment/netfishing_sky.tres b/world/environment/netfishing_sky.tres index 3661dee..f94a6e4 100644 --- a/world/environment/netfishing_sky.tres +++ b/world/environment/netfishing_sky.tres @@ -1,9 +1,36 @@ -[gd_resource type="Sky" load_steps=3 format=3 uid="uid://dersyay71dyat"] +[gd_resource type="Sky" format=3 uid="uid://dersyay71dyat"] -[ext_resource type="Shader" path="res://world/environment/netfishing_sky.gdshader" id="1_sky_shader"] +[ext_resource type="Shader" uid="uid://chbcdlhui3ro4" path="res://world/environment/netfishing_sky.gdshader" id="1_sky_shader"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_netfishing"] shader = ExtResource("1_sky_shader") +shader_parameter/cloud_coverage = 0.0 +shader_parameter/cloud_opacity = 0.0 +shader_parameter/cloud_light_color = Color(0.7, 0.76, 0.77, 1) +shader_parameter/cloud_shadow_color = Color(0.31, 0.38, 0.4, 1) +shader_parameter/cloud_wind = Vector2(0.01, -0.006) +shader_parameter/cloud_scale = 0.72 +shader_parameter/sky_top_color = Color(0.204, 0.498, 0.643, 1) +shader_parameter/sky_horizon_color = Color(0.663, 0.843, 0.847, 1) +shader_parameter/ground_bottom_color = Color(0.157, 0.361, 0.439, 1) +shader_parameter/ground_horizon_color = Color(0.549, 0.749, 0.765, 1) +shader_parameter/horizon_blend_width = 0.01 +shader_parameter/fog_horizon_color = Color(0.13, 0.21, 0.32, 1) +shader_parameter/fog_horizon_occlusion = 0.0 +shader_parameter/sun_direction = Vector3(0, 1, 0) +shader_parameter/sun_color = Color(1, 0.86, 0.46, 1) +shader_parameter/sun_visibility = 1.0 +shader_parameter/sun_radius = 0.055 +shader_parameter/sun_edge_softness = 0.003 +shader_parameter/sun_halo_radius = 0.14 +shader_parameter/sun_halo_strength = 0.16 +shader_parameter/moon_direction = Vector3(0, -1, 0) +shader_parameter/moon_color = Color(0.78, 0.88, 1, 1) +shader_parameter/moon_visibility = 0.0 +shader_parameter/moon_radius = 0.045 +shader_parameter/moon_edge_softness = 0.003 +shader_parameter/moon_halo_radius = 0.11 +shader_parameter/moon_halo_strength = 0.12 [resource] sky_material = SubResource("ShaderMaterial_netfishing") diff --git a/world/environment/netfishing_sky_clouds.gdshaderinc b/world/environment/netfishing_sky_clouds.gdshaderinc new file mode 100644 index 0000000..d0cbbfb --- /dev/null +++ b/world/environment/netfishing_sky_clouds.gdshaderinc @@ -0,0 +1,70 @@ +uniform float cloud_coverage : hint_range(0.0, 1.0, 0.01) = 0.0; +uniform float cloud_opacity : hint_range(0.0, 1.0, 0.01) = 0.0; +uniform vec4 cloud_light_color : source_color = vec4(0.70, 0.76, 0.77, 1.0); +uniform vec4 cloud_shadow_color : source_color = vec4(0.31, 0.38, 0.40, 1.0); +uniform vec2 cloud_wind = vec2(0.010, -0.006); +uniform float cloud_scale : hint_range(0.1, 3.0, 0.05) = 0.72; + + +float cloud_hash(vec2 point) { + return fract(sin(dot(point, vec2(127.1, 311.7))) * 43758.5453); +} + + +float cloud_value_noise(vec2 point) { + vec2 cell = floor(point); + vec2 local = fract(point); + vec2 blend = local * local * (3.0 - 2.0 * local); + float bottom = mix( + cloud_hash(cell), + cloud_hash(cell + vec2(1.0, 0.0)), + blend.x + ); + float top = mix( + cloud_hash(cell + vec2(0.0, 1.0)), + cloud_hash(cell + vec2(1.0, 1.0)), + blend.x + ); + return mix(bottom, top, blend.y); +} + + +float cloud_shape(vec2 point) { + vec2 broad_point = floor(point * 5.0) / 5.0; + vec2 detail_point = floor(point * 13.0) / 13.0; + float broad = cloud_value_noise(broad_point * 0.62); + float detail = cloud_value_noise(detail_point * 1.55 + vec2(17.3, 9.1)); + float combined = broad * 0.72 + detail * 0.28; + return floor(combined * 7.0 + 0.5) / 7.0; +} + + +vec3 apply_weather_clouds(vec3 base_color, vec3 view_direction) { + if (cloud_opacity <= 0.001 || view_direction.y <= 0.0) { + return base_color; + } + float safe_height = max(view_direction.y, 0.08); + vec2 cloud_plane = view_direction.xz / safe_height; + vec2 cloud_point = ( + cloud_plane + cloud_wind * TIME + ) * cloud_scale; + float shape = cloud_shape(cloud_point); + float threshold = mix(0.82, 0.27, cloud_coverage); + float mask = smoothstep(threshold - 0.08, threshold + 0.08, shape); + mask = floor(mask * 4.0 + 0.5) / 4.0; + mask *= smoothstep(0.025, 0.14, view_direction.y); + mask *= cloud_opacity; + float body_depth = smoothstep(threshold, 1.0, shape); + float underside = clamp( + (1.0 - smoothstep(0.08, 0.72, view_direction.y)) * 0.35 + + body_depth * 0.65, + 0.0, + 1.0 + ); + vec3 cloud_color = mix( + cloud_light_color.rgb, + cloud_shadow_color.rgb, + underside + ); + return mix(base_color, cloud_color, mask); +} diff --git a/world/environment/netfishing_sky_clouds.gdshaderinc.uid b/world/environment/netfishing_sky_clouds.gdshaderinc.uid new file mode 100644 index 0000000..cf79ca0 --- /dev/null +++ b/world/environment/netfishing_sky_clouds.gdshaderinc.uid @@ -0,0 +1 @@ +uid://cuqo54hgbjcrf diff --git a/world/fishing_shop_interaction.gd b/world/fishing_shop_interaction.gd index 880b78a..d912eca 100644 --- a/world/fishing_shop_interaction.gd +++ b/world/fishing_shop_interaction.gd @@ -3,17 +3,25 @@ extends Area3D signal local_player_range_changed(in_range: bool) +@export_node_path("Node3D") var look_character_path: NodePath = ^"../Shopkeeper" + var _local_player: Player var _local_player_in_range: bool = false +var _look_character: WorldCharacterDisplay func _ready() -> void: + _look_character = get_node_or_null( + look_character_path + ) as WorldCharacterDisplay body_entered.connect(_on_body_entered) body_exited.connect(_on_body_exited) func setup_local_player(player: Player) -> void: _local_player = player + if _look_character != null: + _look_character.set_head_look_target(player) _refresh_overlaps() @@ -29,6 +37,12 @@ func is_avatar_in_range(avatar: Player) -> bool: ) +func get_prompt_anchor_position() -> Vector3: + if _look_character != null: + return _look_character.get_head_anchor_position() + return global_position + Vector3.UP * 1.25 + + func _refresh_overlaps() -> void: var is_overlapping: bool = ( _local_player != null @@ -49,6 +63,8 @@ func _on_body_exited(body: Node3D) -> void: func _set_local_player_in_range(in_range: bool) -> void: + if _look_character != null: + _look_character.set_head_look_active(in_range) if _local_player_in_range == in_range: return _local_player_in_range = in_range diff --git a/world/generated/shorelines/starter_ocean_shoreline.tres b/world/generated/shorelines/starter_ocean_shoreline.tres index 2481c43..627b08e 100644 --- a/world/generated/shorelines/starter_ocean_shoreline.tres +++ b/world/generated/shorelines/starter_ocean_shoreline.tres @@ -1,14 +1,14 @@ -[gd_resource type="ArrayMesh" format=4] +[gd_resource type="ArrayMesh" format=4 uid="uid://dmgps6rhbdjlw"] [resource] _surfaces = [{ -"aabb": AABB(-28.317474, -0.432, -27.355175, 55.825676, 1e-05, 52.391964), -"attribute_data": PackedByteArray("AAAAAAAAAAAAAAAAAACAP65fYT4AAAAArl9hPgAAgD+QX+E+AAAAAJBf4T4AAIA/swcpPwAAAACzByk/AACAP5BfYT8AAAAAkF9hPwAAgD8o2Yw/AAAAACjZjD8AAIA/HAWpPwAAAAAcBak/AACAPw8xxT8AAAAADzHFPwAAgD/CW+E/AAAAAMJb4T8AAIA/sof9PwAAAACyh/0/AACAP87ZDEAAAAAAztkMQAAAgD/M7BpAAAAAAMzsGkAAAIA/xwIpQAAAAADHAilAAACAP8EYN0AAAAAAwRg3QAAAgD+UIEVAAAAAAJQgRUAAAIA/mvxSQAAAAACa/FJAAACAP5ESYUAAAAAAkRJhQAAAgD+Ps25AAAAAAI+zbkAAAIA/icl8QAAAAACJyXxAAACAP1ZfhUAAAAAAVl+FQAAAgD9RaoxAAAAAAFFqjEAAAIA/T3WTQAAAAABPdZNAAACAP0J7mkAAAAAAQnuaQAAAgD8+hqFAAAAAAD6GoUAAAIA/OpGoQAAAAAA6kahAAACAPzacr0AAAAAANpyvQAAAgD+JlLZAAAAAAImUtkAAAIA/g5+9QAAAAACDn71AAACAP4CqxEAAAAAAgKrEQAAAgD+WjstAAAAAAJaOy0AAAIA/k5nSQAAAAACTmdJAAACAP5ye2UAAAAAAnJ7ZQAAAgD+WqeBAAAAAAJap4EAAAIA/k7TnQAAAAACTtOdAAACAP/y87kAAAAAA/LzuQAAAgD/4x/VAAAAAAPjH9UAAAIA/9NL8QAAAAAD00vxAAACAP/juAUEAAAAA+O4BQQAAgD+scgVBAAAAAKxyBUEAAIA/K/gIQQAAAAAr+AhBAACAP6h9DEEAAAAAqH0MQQAAgD9r+w9BAAAAAGv7D0EAAIA/6YATQQAAAADpgBNBAACAP8/vFkEAAAAAz+8WQQAAgD9NdRpBAAAAAE11GkEAAIA/jdIdQQAAAACN0h1BAACAPwtYIUEAAAAAC1ghQQAAgD9u3CRBAAAAAG7cJEEAAIA/7GEoQQAAAADsYShBAACAP9vlK0EAAAAA2+UrQQAAgD9Yay9BAAAAAFhrL0EAAIA/1vAyQQAAAADW8DJBAACAP8d1NkEAAAAAx3U2QQAAgD9F+zlBAAAAAEX7OUEAAIA/woA9QQAAAADCgD1BAACAPxYGQUEAAAAAFgZBQQAAgD+Ui0RBAAAAAJSLREEAAIA/FBFIQQAAAAAUEUhBAACAP5GWS0EAAAAAkZZLQQAAgD8QHE9BAAAAABAcT0EAAIA/gYZSQQAAAACBhlJBAACAPwAMVkEAAAAAAAxWQQAAgD9+kVlBAAAAAH6RWUEAAIA//RZdQQAAAAD9Fl1BAACAP3qcYEEAAAAAepxgQQAAgD/4IWRBAAAAAPghZEEAAIA/d6dnQQAAAAB3p2dBAACAP/Qsa0EAAAAA9CxrQQAAgD9zsm5BAAAAAHOybkEAAIA/VDRyQQAAAABUNHJBAACAP9O5dUEAAAAA07l1QQAAgD9RP3lBAAAAAFE/eUEAAIA/z8R8QQAAAADPxHxBAACAPyYlgEEAAAAAJiWAQQAAgD/l54FBAAAAAOXngUEAAIA/pKqDQQAAAACkqoNBAACAP2RthUEAAAAAZG2FQQAAgD8jMIdBAAAAACMwh0EAAIA/4vKIQQAAAADi8ohBAACAP6G1ikEAAAAAobWKQQAAgD9geIxBAAAAAGB4jEEAAIA/HzuOQQAAAAAfO45BAACAP979j0EAAAAA3v2PQQAAgD+dwJFBAAAAAJ3AkUEAAIA/XIOTQQAAAABcg5NBAACAPxtGlUEAAAAAG0aVQQAAgD/MCJdBAAAAAMwIl0EAAIA/jMuYQQAAAACMy5hBAACAP0uOmkEAAAAAS46aQQAAgD8KUZxBAAAAAApRnEEAAIA/yROeQQAAAADJE55BAACAP4jWn0EAAAAAiNafQQAAgD9HmaFBAAAAAEeZoUEAAIA/BlyjQQAAAAAGXKNBAACAP8UepUEAAAAAxR6lQQAAgD+E4aZBAAAAAIThpkEAAIA/Q6SoQQAAAABDpKhBAACAPwNnqkEAAAAAA2eqQQAAgD/CKaxBAAAAAMIprEEAAIA/geytQQAAAACB7K1BAACAPz+vr0EAAAAAP6+vQQAAgD/+cbFBAAAAAP5xsUEAAIA/vjSzQQAAAAC+NLNBAACAP333tEEAAAAAffe0QQAAgD88urZBAAAAADy6tkEAAIA/+3y4QQAAAAD7fLhBAACAP7o/ukEAAAAAuj+6QQAAgD95ArxBAAAAAHkCvEEAAIA/OMW9QQAAAAA4xb1BAACAP+CHv0EAAAAA4Ie/QQAAgD+fSsFBAAAAAJ9KwUEAAIA/Xg3DQQAAAABeDcNBAACAPx3QxEEAAAAAHdDEQQAAgD/cksZBAAAAANySxkEAAIA/m1XIQQAAAACbVchBAACAP1oYykEAAAAAWhjKQQAAgD8Z28tBAAAAABnby0EAAIA/2J3NQQAAAADYnc1BAACAP5dgz0EAAAAAl2DPQQAAgD9WI9FBAAAAAFYj0UEAAIA/FebSQQAAAAAV5tJBAACAP9So1EEAAAAA1KjUQQAAgD+Ta9ZBAAAAAJNr1kEAAIA/Ui7YQQAAAABSLthBAACAPxLx2UEAAAAAEvHZQQAAgD/Rs9tBAAAAANGz20EAAIA/kHbdQQAAAACQdt1BAACAP08530EAAAAATznfQQAAgD8O/OBBAAAAAA784EEAAIA/zb7iQQAAAADNvuJBAACAP4yB5EEAAAAAjIHkQQAAgD9LROZBAAAAAEtE5kEAAIA/CgfoQQAAAAAKB+hBAACAP8nJ6UEAAAAAycnpQQAAgD+IjOtBAAAAAIiM60EAAIA/R0/tQQAAAABHT+1BAACAPwYS70EAAAAABhLvQQAAgD/F1PBBAAAAAMXU8EEAAIA/hJfyQQAAAACEl/JBAACAP0Ra9EEAAAAARFr0QQAAgD/+HPZBAAAAAP4c9kEAAIA/vd/3QQAAAAC93/dBAACAP3yi+UEAAAAAfKL5QQAAgD87ZftBAAAAADtl+0EAAIA/+if9QQAAAAD6J/1BAACAP7nq/kEAAAAAuer+QQAAgD+8VgBCAAAAALxWAEIAAIA/HDgBQgAAAAAcOAFCAACAP3sZAkIAAAAAexkCQgAAgD/b+gJCAAAAANv6AkIAAIA/OtwDQgAAAAA63ANCAACAP5q9BEIAAAAAmr0EQgAAgD/5ngVCAAAAAPmeBUIAAIA/WYAGQgAAAABZgAZCAACAP7hhB0IAAAAAuGEHQgAAgD8YQwhCAAAAABhDCEIAAIA/dyQJQgAAAAB3JAlCAACAP9cFCkIAAAAA1wUKQgAAgD825wpCAAAAADbnCkIAAIA/lsgLQgAAAACWyAtCAACAP/WpDEIAAAAA9akMQgAAgD9Viw1CAAAAAFWLDUIAAIA/tGwOQgAAAAC0bA5CAACAPwZOD0IAAAAABk4PQgAAgD9lLxBCAAAAAGUvEEIAAIA/xRARQgAAAADFEBFCAACAPyTyEUIAAAAAJPIRQgAAgD+E0xJCAAAAAITTEkIAAIA/47QTQgAAAADjtBNCAACAP0OWFEIAAAAAQ5YUQgAAgD+idxVCAAAAAKJ3FUIAAIA/AlkWQgAAAAACWRZCAACAP2E6F0IAAAAAYToXQgAAgD/BGxhCAAAAAMEbGEIAAIA/IP0YQgAAAAAg/RhCAACAP4DeGUIAAAAAgN4ZQgAAgD/gvxpCAAAAAOC/GkIAAIA/P6EbQgAAAAA/oRtCAACAP56CHEIAAAAAnoIcQgAAgD+lYx1CAAAAAKVjHUIAAIA/BUUeQgAAAAAFRR5CAACAP2QmH0IAAAAAZCYfQgAAgD/EByBCAAAAAMQHIEIAAIA/I+kgQgAAAAAj6SBCAACAP4PKIUIAAAAAg8ohQgAAgD/iqyJCAAAAAOKrIkIAAIA/Qo0jQgAAAABCjSNCAACAP6FuJEIAAAAAoW4kQgAAgD9tPyVCAAAAAG0/JUIAAIA/zSAmQgAAAADNICZCAACAPywCJ0IAAAAALAInQgAAgD9N4SdCAAAAAE3hJ0IAAIA/rcIoQgAAAACtwihCAACAPwykKUIAAAAADKQpQgAAgD9shSpCAAAAAGyFKkIAAIA/O2YrQgAAAAA7ZitCAACAP5tHLEIAAAAAm0csQgAAgD/6KC1CAAAAAPooLUIAAIA/WgouQgAAAABaCi5CAACAP7nrLkIAAAAAuesuQgAAgD8ZzS9CAAAAABnNL0IAAIA/ZK4wQgAAAABkrjBCAACAP8OPMUIAAAAAw48xQgAAgD8jcTJCAAAAACNxMkIAAIA/g1IzQgAAAACDUjNCAACAP+IzNEIAAAAA4jM0QgAAgD9BFTVCAAAAAEEVNUIAAIA/ofY1QgAAAACh9jVCAACAP+/XNkIAAAAA79c2QgAAgD9PuTdCAAAAAE+5N0IAAIA/rpo4QgAAAACumjhCAACAPw58OUIAAAAADnw5QgAAgD9tXTpCAAAAAG1dOkIAAIA/zT47QgAAAADNPjtCAACAPywgPEIAAAAALCA8QgAAgD+MAT1CAAAAAIwBPUIAAIA/6+I9QgAAAADr4j1CAACAPyzEPkIAAAAALMQ+QgAAgD+MpT9CAAAAAIylP0IAAIA/7IZAQgAAAADshkBCAACAP0toQUIAAAAAS2hBQgAAgD+rSUJCAAAAAKtJQkIAAIA/CitDQgAAAAAKK0NCAACAP2oMREIAAAAAagxEQgAAgD+27URCAAAAALbtREIAAIA/Fc9FQgAAAAAVz0VCAACAP3WwRkIAAAAAdbBGQgAAgD/UkUdCAAAAANSRR0IAAIA/NHNIQgAAAAA0c0hCAACAP5NUSUIAAAAAk1RJQgAAgD91NEpCAAAAAHU0SkIAAIA/1RVLQgAAAADVFUtCAACAPzT3S0IAAAAANPdLQgAAgD/X0ExCAAAAANfQTEIAAIA/N7JNQgAAAAA3sk1CAACAP5aTTkIAAAAAlpNOQgAAgD95dE9CAAAAAHl0T0IAAIA/2VVQQgAAAADZVVBCAACAPyk3UUIAAAAAKTdRQgAAgD+JGFJCAAAAAIkYUkIAAIA/6PlSQgAAAADo+VJCAACAP0jbU0IAAAAASNtTQgAAgD+ivFRCAAAAAKK8VEIAAIA/AZ5VQgAAAAABnlVCAACAP2F/VkIAAAAAYX9WQgAAgD+SYFdCAAAAAJJgV0IAAIA/8UFYQgAAAADxQVhCAACAP1EjWUIAAAAAUSNZQgAAgD+wBFpCAAAAALAEWkIAAIA/4OVaQgAAAADg5VpCAACAP0DHW0IAAAAAQMdbQgAAgD+fqFxCAAAAAJ+oXEIAAIA/qoldQgAAAACqiV1CAACAPwprXkIAAAAACmteQgAAgD8DS19CAAAAAANLX0IAAIA/jBpgQgAAAACMGmBCAACAPyb4YEIAAAAAJvhgQgAAgD+F1mFCAAAAAIXWYUIAAIA/v7ZiQgAAAAC/tmJCAACAPx+YY0IAAAAAH5hjQgAAgD/keGRCAAAAAOR4ZEIAAIA/RFplQgAAAABEWmVCAACAP5U7ZkIAAAAAlTtmQgAAgD/dG2dCAAAAAN0bZ0IAAIA/bfpnQgAAAABt+mdCAACAP8fVaEIAAAAAx9VoQgAAgD9TsWlCAAAAAFOxaUIAAIA/F5FqQgAAAAAXkWpCAACAP3Zya0IAAAAAdnJrQgAAgD/MU2xCAAAAAMxTbEIAAIA/LDVtQgAAAAAsNW1CAACAP3QWbkIAAAAAdBZuQgAAgD/U925CAAAAANT3bkIAAIA/FdlvQgAAAAAV2W9CAACAP3S6cEIAAAAAdLpwQgAAgD/Um3FCAAAAANSbcUIAAIA/33xyQgAAAADffHJCAACAPz5ec0IAAAAAPl5zQgAAgD/0PnRCAAAAAPQ+dEIAAIA/VCB1QgAAAABUIHVCAACAP1QBdkIAAAAAVAF2QgAAgD8r4HZCAAAAACvgdkIAAIA/i8F3QgAAAACLwXdCAACAP0aheEIAAAAARqF4QgAAgD+mgnlCAAAAAKaCeUIAAIA/KWN6QgAAAAApY3pCAACAP4hEe0IAAAAAiER7QgAAgD/AJXxCAAAAAMAlfEIAAIA/Hwd9QgAAAAAfB31CAACAP3/ofUIAAAAAf+h9QgAAgD+XyX5CAAAAAJfJfkIAAIA/96p/QgAAAAD3qn9CAACAPytGgEIAAAAAK0aAQgAAgD/PtoBCAAAAAM+2gEIAAIA/fyeBQgAAAAB/J4FCAACAPy6YgUIAAAAALpiBQgAAgD/eCIJCAAAAAN4IgkIAAIA/d3mCQgAAAAB3eYJCAACAPyfqgkIAAAAAJ+qCQgAAgD/XWoNCAAAAANdag0IAAIA/h8uDQgAAAACHy4NCAACAPzI8hEIAAAAAMjyEQgAAgD/irIRCAAAAAOKshEIAAIA/kh2FQgAAAACSHYVCAACAP0GOhUIAAAAAQY6FQgAAgD+g/oVCAAAAAKD+hUIAAIA/UG+GQgAAAABQb4ZCAACAPwDghkIAAAAAAOCGQgAAgD+wUIdCAAAAALBQh0IAAIA/X8GHQgAAAABfwYdCAACAPw8yiEIAAAAADzKIQgAAgD+/oohCAAAAAL+iiEIAAIA/bxOJQgAAAABvE4lCAACAPxSEiUIAAAAAFISJQgAAgD/E9IlCAAAAAMT0iUIAAIA/dGWKQgAAAAB0ZYpCAACAPyPWikIAAAAAI9aKQgAAgD/TRotCAAAAANNGi0IAAIA/g7eLQgAAAACDt4tCAACAPzMojEIAAAAAMyiMQgAAgD/imIxCAAAAAOKYjEIAAIA/kgmNQgAAAACSCY1CAACAP0J6jUIAAAAAQnqNQgAAgD/y6o1CAAAAAPLqjUIAAIA/oVuOQgAAAAChW45CAACAP1HMjkIAAAAAUcyOQgAAgD8BPY9CAAAAAAE9j0IAAIA/r62PQgAAAACvrY9CAACAP18ekEIAAAAAXx6QQgAAgD8Pj5BCAAAAAA+PkEIAAIA/vv+QQgAAAAC+/5BCAACAP25wkUIAAAAAbnCRQgAAgD8e4ZFCAAAAAB7hkUIAAIA/zlGSQgAAAADOUZJCAACAP33CkkIAAAAAfcKSQgAAgD8tM5NCAAAAAC0zk0IAAIA/3aOTQgAAAADdo5NCAACAP40UlEIAAAAAjRSUQgAAgD89hZRCAAAAAD2FlEIAAIA/7PWUQgAAAADs9ZRCAACAP5xmlUIAAAAAnGaVQgAAgD9M15VCAAAAAEzXlUIAAIA//EeWQgAAAAD8R5ZCAACAP6u4lkIAAAAAq7iWQgAAgD9bKZdCAAAAAFspl0IAAIA/CpqXQgAAAAAKmpdCAACAP7oKmEIAAAAAugqYQgAAgD9qe5hCAAAAAGp7mEIAAIA/GeyYQgAAAAAZ7JhCAACAP8lcmUIAAAAAyVyZQgAAgD95zZlCAAAAAHnNmUIAAIA/KT6aQgAAAAApPppCAACAP9mumkIAAAAA2a6aQgAAgD+IH5tCAAAAAIgfm0IAAIA/OJCbQgAAAAA4kJtCAACAP+gAnEIAAAAA6ACcQgAAgD+YcZxCAAAAAJhxnEIAAIA/R+KcQgAAAABH4pxCAACAP/dSnUIAAAAA91KdQgAAgD+nw51CAAAAAKfDnUIAAIA/VzSeQgAAAABXNJ5CAACAPwalnkIAAAAABqWeQgAAgD+2FZ9CAAAAALYVn0IAAIA/ZoafQgAAAABmhp9CAACAPxb3n0IAAAAAFvefQgAAgD/FZ6BCAAAAAMVnoEIAAIA/ddigQgAAAAB12KBCAACAPyVJoUIAAAAAJUmhQgAAgD/VuaFCAAAAANW5oUIAAIA/hCqiQgAAAACEKqJCAACAPzSbokIAAAAANJuiQgAAgD/kC6NCAAAAAOQLo0IAAIA/lHyjQgAAAACUfKNCAACAP0Tto0IAAAAARO2jQgAAgD/zXaRCAAAAAPNdpEIAAIA/o86kQgAAAACjzqRCAACAP1M/pUIAAAAAUz+lQgAAgD8DsKVCAAAAAAOwpUIAAIA/siCmQgAAAACyIKZCAACAP2KRpkIAAAAAYpGmQgAAgD8SAqdCAAAAABICp0IAAIA/wnKnQgAAAADCcqdCAACAP3Ljp0IAAAAAcuOnQgAAgD8hVKhCAAAAACFUqEIAAIA/0cSoQgAAAADRxKhCAACAP4E1qUIAAAAAgTWpQgAAgD8wpqlCAAAAADCmqUIAAIA/4BaqQgAAAADgFqpCAACAP5CHqkIAAAAAkIeqQgAAgD9A+KpCAAAAAED4qkIAAIA/8GirQgAAAADwaKtCAACAP5/Zq0IAAAAAn9mrQgAAgD9PSqxCAAAAAE9KrEIAAIA//7qsQgAAAAD/uqxCAACAP68rrUIAAAAAryutQgAAgD9enK1CAAAAAF6crUIAAIA/Dg2uQgAAAAAODa5CAACAP759rkIAAAAAvn2uQgAAgD9u7q5CAAAAAG7urkIAAIA/HF+vQgAAAAAcX69CAACAP8zPr0IAAAAAzM+vQgAAgD97QLBCAAAAAHtAsEIAAIA/K7GwQgAAAAArsbBCAACAP9shsUIAAAAA2yGxQgAAgD+LkrFCAAAAAIuSsUIAAIA/OgOyQgAAAAA6A7JCAACAP+pzskIAAAAA6nOyQgAAgD+S5LJCAAAAAJLkskIAAIA/QVWzQgAAAABBVbNCAACAP/HFs0IAAAAA8cWzQgAAgD+hNrRCAAAAAKE2tEIAAIA/TKe0QgAAAABMp7RCAACAP/sXtUIAAAAA+xe1QgAAgD+riLVCAAAAAKuItUIAAIA/W/m1QgAAAABb+bVCAACAPwtqtkIAAAAAC2q2QgAAgD+42rZCAAAAALjatkIAAIA/Z0u3QgAAAABnS7dCAACAPxe8t0IAAAAAF7y3QgAAgD/HLLhCAAAAAMcsuEIAAIA/d524QgAAAAB3nbhCAACAPyYOuUIAAAAAJg65QgAAgD/VfrlCAAAAANV+uUIAAIA/he+5QgAAAACF77lCAACAPzRgukIAAAAANGC6QgAAgD/k0LpCAAAAAOTQukIAAIA/lEG7QgAAAACUQbtCAACAP0Syu0IAAAAARLK7QgAAgD/0IrxCAAAAAPQivEIAAIA/o5O8QgAAAACjk7xCAACAP1MEvUIAAAAAUwS9QgAAgD8Cdb1CAAAAAAJ1vUIAAIA/suW9QgAAAACy5b1CAACAP2JWvkIAAAAAYla+QgAAgD8Rx75CAAAAABHHvkIAAIA/wTe/QgAAAADBN79CAACAP3Gov0IAAAAAcai/QgAAgD/aF8BCAAAAANoXwEIAAIA/iojAQgAAAACKiMBCAACAPzr5wEIAAAAAOvnAQgAAgD/qacFCAAAAAOppwUIAAIA/mdrBQgAAAACZ2sFCAACAP0lLwkIAAAAASUvCQgAAgD8iu8JCAAAAACK7wkIAAIA/0ivDQgAAAADSK8NCAACAP4Gcw0IAAAAAgZzDQgAAgD8xDcRCAAAAADENxEIAAIA/4X3EQgAAAADhfcRCAACAP5HuxEIAAAAAke7EQgAAgD8pXMVCAAAAAClcxUIAAIA/2czFQgAAAADZzMVCAACAP4k9xkIAAAAAiT3GQgAAgD85rsZCAAAAADmuxkIAAIA/6B7HQgAAAADoHsdCAACAP5iPx0IAAAAAmI/HQgAAgD+y/sdCAAAAALL+x0IAAIA/Ym/IQgAAAABib8hCAACAPxLgyEIAAAAAEuDIQgAAgD/CUMlCAAAAAMJQyUIAAIA/ccHJQgAAAABxwclCAACAPyEyykIAAAAAITLKQgAAgD/RospCAAAAANGiykIAAIA/gRPLQgAAAACBE8tCAACAPwaEy0IAAAAABoTLQgAAgD+19MtCAAAAALX0y0IAAIA/ZWXMQgAAAABlZcxCAACAPxXWzEIAAAAAFdbMQgAAgD/FRs1CAAAAAMVGzUIAAIA/dLfNQgAAAAB0t81CAACAPyIozkIAAAAAIijOQgAAgD/SmM5CAAAAANKYzkIAAIA/ggnPQgAAAACCCc9CAACAPzF6z0IAAAAAMXrPQgAAgD/h6s9CAAAAAOHqz0IAAIA/kVvQQgAAAACRW9BCAACAP0HM0EIAAAAAQczQQgAAgD91PNFCAAAAAHU80UIAAIA/JK3RQgAAAAAkrdFCAACAP9Qd0kIAAAAA1B3SQgAAgD+EjtJCAAAAAISO0kIAAIA/NP/SQgAAAAA0/9JCAACAP+Rv00IAAAAA5G/TQgAAgD8d4NNCAAAAAB3g00IAAIA/zVDUQgAAAADNUNRCAACAP33B1EIAAAAAfcHUQgAAgD8tMtVCAAAAAC0y1UIAAIA/3KLVQgAAAADcotVCAACAP4wT1kIAAAAAjBPWQgAAgD8ohNZCAAAAACiE1kIAAIA/2PTWQgAAAADY9NZCAACAP4hl10IAAAAAiGXXQgAAgD841tdCAAAAADjW10IAAIA/50bYQgAAAADnRthCAACAP1C32EIAAAAAULfYQgAAgD8AKNlCAAAAAAAo2UIAAIA/sJjZQgAAAACwmNlCAACAP18J2kIAAAAAXwnaQgAAgD/PedpCAAAAAM952kIAAIA/f+raQgAAAAB/6tpCAACAPy9b20IAAAAAL1vbQgAAgD/ey9tCAAAAAN7L20IAAIA/bjzcQgAAAABuPNxCAACAPx6t3EIAAAAAHq3cQgAAgD/OHd1CAAAAAM4d3UIAAIA/fo7dQgAAAAB+jt1CAACAPyr/3UIAAAAAKv/dQgAAgD/ab95CAAAAANpv3kIAAIA/ieDeQgAAAACJ4N5CAACAPzlR30IAAAAAOVHfQgAAgD/pwd9CAAAAAOnB30IAAIA/lzLgQgAAAACXMuBCAACAP0aj4EIAAAAARqPgQgAAgD/2E+FCAAAAAPYT4UIAAIA/poThQgAAAACmhOFCAACAP1b14UIAAAAAVvXhQgAAgD8FZuJCAAAAAAVm4kIAAIA/tNbiQgAAAAC01uJCAACAP2RH40IAAAAAZEfjQgAAgD8UuONCAAAAABS440IAAIA/xCjkQgAAAADEKORCAACAP3OZ5EIAAAAAc5nkQgAAgD8jCuVCAAAAACMK5UIAAIA/03rlQgAAAADTeuVCAACAP4Lr5UIAAAAAguvlQgAAgD8yXOZCAAAAADJc5kIAAIA/4szmQgAAAADizOZCAACAP5I950IAAAAAkj3nQgAAgD9BrudCAAAAAEGu50IAAIA/8R7oQgAAAADxHuhCAACAP6GP6EIAAAAAoY/oQgAAgD9CAOlCAAAAAEIA6UIAAIA/8XDpQgAAAADxcOlCAACAP6Hh6UIAAAAAoeHpQgAAgD9RUupCAAAAAFFS6kIAAIA/AcPqQgAAAAABw+pCAACAP7Az60IAAAAAsDPrQgAAgD9BpOtCAAAAAEGk60IAAIA/8RTsQgAAAADxFOxCAACAP6CF7EIAAAAAoIXsQgAAgD9Q9uxCAAAAAFD27EIAAIA/AGftQgAAAAAAZ+1CAACAP0fX7UIAAAAAR9ftQgAAgD/3R+5CAAAAAPdH7kIAAIA/prjuQgAAAACmuO5CAACAP1Yp70IAAAAAVinvQgAAgD/vmO9CAAAAAO+Y70IAAIA/ngnwQgAAAACeCfBCAACAP0h68EIAAAAASHrwQgAAgD/46vBCAAAAAPjq8EIAAIA/p1vxQgAAAACnW/FCAACAP0rM8UIAAAAASszxQgAAgD/6PPJCAAAAAPo88kIAAIA/kK3yQgAAAACQrfJCAACAP0Ae80IAAAAAQB7zQgAAgD/LjvNCAAAAAMuO80IAAIA/e//zQgAAAAB7//NCAACAPyNw9EIAAAAAI3D0QgAAgD+94PRCAAAAAL3g9EIAAIA/bVH1QgAAAABtUfVCAACAP/zB9UIAAAAA/MH1QgAAgD+sMvZCAAAAAKwy9kIAAIA/U6P2QgAAAABTo/ZCAACAP4gT90IAAAAAiBP3QgAAgD84hPdCAAAAADiE90IAAIA/lPT3QgAAAACU9PdCAACAP0Rl+EIAAAAARGX4QgAAgD/H1fhCAAAAAMfV+EIAAIA/d0b5QgAAAAB3RvlCAACAPye3+UIAAAAAJ7f5QgAAgD/OJ/pCAAAAAM4n+kIAAIA/fpj6QgAAAAB+mPpCAACAPyoJ+0IAAAAAKgn7QgAAgD/ZeftCAAAAANl5+0IAAIA/ier7QgAAAACJ6vtCAACAPzJb/EIAAAAAMlv8QgAAgD/hy/xCAAAAAOHL/EIAAIA/kTz9QgAAAACRPP1CAACAPzqt/UIAAAAAOq39QgAAgD/qHf5CAAAAAOod/kIAAIA/mY7+QgAAAACZjv5CAACAP0b//kIAAAAARv/+QgAAgD/2b/9CAAAAAPZv/0IAAIA/auD/QgAAAABq4P9CAACAP40oAEMAAAAAjSgAQwAAgD/lYABDAAAAAOVgAEMAAIA/PZkAQwAAAAA9mQBDAACAP5XRAEMAAAAAldEAQwAAgD/nCQFDAAAAAOcJAUMAAIA/P0IBQwAAAAA/QgFDAACAP5d6AUMAAAAAl3oBQwAAgD/vsgFDAAAAAO+yAUMAAIA/R+sBQwAAAABH6wFDAACAP5sjAkMAAAAAmyMCQwAAgD/zWwJDAAAAAPNbAkMAAIA/S5QCQwAAAABLlAJDAACAP6PMAkMAAAAAo8wCQwAAgD/6BANDAAAAAPoEA0MAAIA/Uj0DQwAAAABSPQNDAACAP6p1A0MAAAAAqnUDQwAAgD8CrgNDAAAAAAKuA0MAAIA/WeYDQwAAAABZ5gNDAACAP7EeBEMAAAAAsR4EQwAAgD8JVwRDAAAAAAlXBEMAAIA/YY8EQwAAAABhjwRDAACAP7nHBEMAAAAAuccEQwAAgD8RAAVDAAAAABEABUMAAIA/aTgFQwAAAABpOAVDAACAP8FwBUMAAAAAwXAFQwAAgD8IqQVDAAAAAAipBUMAAIA/YOEFQwAAAABg4QVDAACAP7gZBkMAAAAAuBkGQwAAgD8QUgZDAAAAABBSBkMAAIA/aIoGQwAAAABoigZDAACAP7/CBkMAAAAAv8IGQwAAgD8X+wZDAAAAABf7BkMAAIA/bzMHQwAAAABvMwdDAACAP4ZrB0MAAAAAhmsHQwAAgD/eowdDAAAAAN6jB0MAAIA/NtwHQwAAAAA23AdDAACAP44UCEMAAAAAjhQIQwAAgD/mTAhDAAAAAOZMCEMAAIA/PoUIQwAAAAA+hQhDAACAP5W9CEMAAAAAlb0IQwAAgD+d9QhDAAAAAJ31CEMAAIA/9C0JQwAAAAD0LQlDAACAP0xmCUMAAAAATGYJQwAAgD+knglDAAAAAKSeCUMAAIA//NYJQwAAAAD81glDAACAP1QPCkMAAAAAVA8KQwAAgD8uRwpDAAAAAC5HCkMAAIA/hn8KQwAAAACGfwpDAACAP923CkMAAAAA3bcKQwAAgD818ApDAAAAADXwCkMAAIA/jSgLQwAAAACNKAtDAACAP+VgC0MAAAAA5WALQwAAgD8qmQtDAAAAACqZC0MAAIA/gtELQwAAAACC0QtDAACAP9oJDEMAAAAA2gkMQwAAgD8xQgxDAAAAADFCDEMAAIA/iXoMQwAAAACJegxDAACAP7iyDEMAAAAAuLIMQwAAgD8Q6wxDAAAAABDrDEMAAIA/aCMNQwAAAABoIw1DAACAP8BbDUMAAAAAwFsNQwAAgD8YlA1DAAAAABiUDUMAAIA/ZcwNQwAAAABlzA1DAACAP70EDkMAAAAAvQQOQwAAgD8VPQ5DAAAAABU9DkMAAIA/bHUOQwAAAABsdQ5DAACAP4+tDkMAAAAAj60OQwAAgD/n5Q5DAAAAAOflDkMAAIA/Px4PQwAAAAA/Hg9DAACAP5ZWD0MAAAAAllYPQwAAgD/ujg9DAAAAAO6OD0MAAIA/RscPQwAAAABGxw9DAACAP57/D0MAAAAAnv8PQwAAgD/qNxBDAAAAAOo3EEMAAIA/QnAQQwAAAABCcBBDAACAP5qoEEMAAAAAmqgQQwAAgD/i4BBDAAAAAOLgEEMAAIA/OhkRQwAAAAA6GRFDAACAP5JREUMAAAAAklERQwAAgD/PiRFDAAAAAM+JEUMAAIA/J8IRQwAAAAAnwhFDAACAP3/6EUMAAAAAf/oRQwAAgD/QMhJDAAAAANAyEkMAAIA/KGsSQwAAAAAoaxJDAACAPxmjEkMAAAAAGaMSQwAAgD9x2xJDAAAAAHHbEkMAAIA/wBMTQwAAAADAExNDAACAPxhME0MAAAAAGEwTQwAAgD9vhBNDAAAAAG+EE0MAAIA/kbwTQwAAAACRvBNDAACAP+n0E0MAAAAA6fQTQwAAgD9BLRRDAAAAAEEtFEMAAIA/l2UUQwAAAACXZRRDAACAP++dFEMAAAAA750UQwAAgD9H1hRDAAAAAEfWFEMAAIA/ng4VQwAAAACeDhVDAACAP+tGFUMAAAAA60YVQwAAgD9DfxVDAAAAAEN/FUMAAIA/Z7cVQwAAAABntxVDAACAP7fvFUMAAAAAt+8VQwAAgD8LKBZDAAAAAAsoFkMAAIA/YmAWQwAAAABiYBZDAACAP7qYFkMAAAAAupgWQwAAgD8E0RZDAAAAAATRFkMAAIA/XAkXQwAAAABcCRdDAACAP7RBF0MAAAAAtEEXQwAAgD8MehdDAAAAAAx6F0MAAIA/Y7IXQwAAAABjshdDAACAP7vqF0MAAAAAu+oXQwAAgD8RIxhDAAAAABEjGEMAAIA/aFsYQwAAAABoWxhDAACAP8CTGEMAAAAAwJMYQwAAgD8NzBhDAAAAAA3MGEMAAIA/ZAQZQwAAAABkBBlDAACAP7w8GUMAAAAAvDwZQwAAgD8IdRlDAAAAAAh1GUMAAIA/YK0ZQwAAAABgrRlDAACAP7flGUMAAAAAt+UZQwAAgD8PHhpDAAAAAA8eGkMAAIA/YlYaQwAAAABiVhpDAACAP7mOGkMAAAAAuY4aQwAAgD8RxxpDAAAAABHHGkMAAIA/af8aQwAAAABp/xpDAACAP8E3G0MAAAAAwTcbQwAAgD8WcBtDAAAAABZwG0MAAIA/bqgbQwAAAABuqBtDAACAP8XgG0MAAAAAxeAbQwAAgD8dGRxDAAAAAB0ZHEMAAIA/dVEcQwAAAAB1URxDAACAP82JHEMAAAAAzYkcQwAAgD/1wRxDAAAAAPXBHEMAAIA/TfocQwAAAABN+hxDAACAP6UyHUMAAAAApTIdQwAAgD/9ah1DAAAAAP1qHUMAAIA/eKIdQwAAAAB4oh1DAACAP8/aHUMAAAAAz9odQwAAgD8nEx5DAAAAACcTHkMAAIA/f0seQwAAAAB/Sx5DAACAP16DHkMAAAAAXoMeQwAAgD+2ux5DAAAAALa7HkMAAIA/DvQeQwAAAAAO9B5DAACAPyQrH0MAAAAAJCsfQwAAgD98Yx9DAAAAAHxjH0MAAIA/1JsfQwAAAADUmx9DAACAPyvUH0MAAAAAK9QfQwAAgD8wDCBDAAAAADAMIEMAAIA/iEQgQwAAAACIRCBDAACAP+B8IEMAAAAA4HwgQwAAgD9wtCBDAAAAAHC0IEMAAIA/yOwgQwAAAADI7CBDAACAP3MkIUMAAAAAcyQhQwAAgD/LXCFDAAAAAMtcIUMAAIA/IpUhQwAAAAAilSFDAACAP/jMIUMAAAAA+MwhQwAAgD9PBSJDAAAAAE8FIkMAAIA/pz0iQwAAAACnPSJDAACAP/91IkMAAAAA/3UiQwAAgD8mriJDAAAAACauIkMAAIA/fuYiQwAAAAB+5iJDAACAP9UeI0MAAAAA1R4jQwAAgD/LViNDAAAAAMtWI0MAAIA/I48jQwAAAAAjjyNDAACAPy7GI0MAAAAALsYjQwAAgD+C/SNDAAAAAIL9I0MAAIA/2jUkQwAAAADaNSRDAACAP+RtJEMAAAAA5G0kQwAAgD88piRDAAAAADymJEMAAIA/Td4kQwAAAABN3iRDAACAP6UWJUMAAAAApRYlQwAAgD/QTiVDAAAAANBOJUMAAIA/KIclQwAAAAAohyVDAACAP3+/JUMAAAAAf78lQwAAgD/X9yVDAAAAANf3JUMAAIA/LzAmQwAAAAAvMCZDAACAPwdoJkMAAAAAB2gmQwAAgD9foCZDAAAAAF+gJkMAAIA/t9gmQwAAAAC32CZDAACAP78QJ0MAAAAAvxAnQwAAgD8XSSdDAAAAABdJJ0MAAIA/boEnQwAAAABugSdDAACAP5O5J0MAAAAAk7knQwAAgD/q8SdDAAAAAOrxJ0MAAIA/QiooQwAAAABCKihDAACAP4diKEMAAAAAh2IoQwAAgD/fmihDAAAAAN+aKEMAAIA/N9MoQwAAAAA30yhDAACAP44LKUMAAAAAjgspQwAAgD/RQylDAAAAANFDKUMAAIA/KXwpQwAAAAApfClDAACAP4G0KUMAAAAAgbQpQwAAgD/Z7ClDAAAAANnsKUMAAIA/+yQqQwAAAAD7JCpDAACAP1NdKkMAAAAAU10qQwAAgD+rlSpDAAAAAKuVKkMAAIA/ps0qQwAAAACmzSpDAACAP/4FK0MAAAAA/gUrQwAAgD9WPitDAAAAAFY+K0MAAIA/iHYrQwAAAACIditDAACAP+CuK0MAAAAA4K4rQwAAgD+M5itDAAAAAIzmK0MAAIA/5B4sQwAAAADkHixDAACAPzxXLEMAAAAAPFcsQwAAgD9ujyxDAAAAAG6PLEMAAIA/xccsQwAAAADFxyxDAACAPx0ALUMAAAAAHQAtQwAAgD91OC1DAAAAAHU4LUMAAIA/unAtQwAAAAC6cC1DAACAPxGpLUMAAAAAEaktQwAAgD9p4S1DAAAAAGnhLUMAAIA/wRkuQwAAAADBGS5DAACAPxlSLkMAAAAAGVIuQwAAgD9jii5DAAAAAGOKLkMAAIA/u8IuQwAAAAC7wi5DAACAPxP7LkMAAAAAE/suQwAAgD9rMy9DAAAAAGszL0MAAIA/w2svQwAAAADDay9DAACAPxukL0MAAAAAG6QvQwAAgD9Z3C9DAAAAAFncL0MAAIA/sBQwQwAAAACwFDBDAACAPwhNMEMAAAAACE0wQwAAgD9ghTBDAAAAAGCFMEMAAIA/uL0wQwAAAAC4vTBDAACAPxD2MEMAAAAAEPYwQwAAgD9PLjFDAAAAAE8uMUMAAIA/p2YxQwAAAACnZjFDAACAP/+eMUMAAAAA/54xQwAAgD9X1zFDAAAAAFfXMUMAAIA/rw8yQwAAAACvDzJDAACAP/hHMkMAAAAA+EcyQwAAgD9QgDJDAAAAAFCAMkMAAIA/qLgyQwAAAACouDJDAACAPwDxMkMAAAAAAPEyQwAAgD9XKTNDAAAAAFcpM0MAAIA/r2EzQwAAAACvYTNDAACAP+mZM0MAAAAA6ZkzQwAAgD9B0jNDAAAAAEHSM0MAAIA/mQo0QwAAAACZCjRDAACAP/FCNEMAAAAA8UI0QwAAgD9JezRDAAAAAEl7NEMAAIA/obM0QwAAAAChszRDAACAPzLrNEMAAAAAMus0QwAAgD+KIzVDAAAAAIojNUMAAIA/4ls1QwAAAADiWzVDAACAPzqUNUMAAAAAOpQ1QwAAgD+SzDVDAAAAAJLMNUMAAIA/6gQ2QwAAAADqBDZDAACAPxI9NkMAAAAAEj02QwAAgD9qdTZDAAAAAGp1NkMAAIA/wq02QwAAAADCrTZDAACAPxrmNkMAAAAAGuY2QwAAgD9yHjdDAAAAAHIeN0MAAIA/ylY3QwAAAADKVjdDAACAPyKPN0MAAAAAIo83QwAAgD96xzdDAAAAAHrHN0MAAIA/0v83QwAAAADS/zdDAACAPyk4OEMAAAAAKTg4QwAAgD9rcDhDAAAAAGtwOEMAAIA/w6g4QwAAAADDqDhDAACAPxvhOEMAAAAAG+E4QwAAgD9zGTlDAAAAAHMZOUMAAIA/ylE5QwAAAADKUTlDAACAPyKKOUMAAAAAIoo5QwAAgD96wjlDAAAAAHrCOUMAAIA/0vo5QwAAAADS+jlDAACAPyozOkMAAAAAKjM6QwAAgD+CazpDAAAAAIJrOkMAAIA/2qM6QwAAAADaozpDAACAPzLcOkMAAAAAMtw6QwAAgD99FDtDAAAAAH0UO0MAAIA/1Uw7QwAAAADVTDtDAACAPy2FO0MAAAAALYU7QwAAgD+FvTtDAAAAAIW9O0MAAIA/3fU7QwAAAADd9TtDAACAPzUuPEMAAAAANS48QwAAgD+MZjxDAAAAAIxmPEMAAIA/5J48QwAAAADknjxDAACAPzzXPEMAAAAAPNc8QwAAgD+UDz1DAAAAAJQPPUMAAIA/7Ec9QwAAAADsRz1DAACAP0SAPUMAAAAARIA9QwAAgD+cuD1DAAAAAJy4PUMAAIA/9PA9QwAAAAD08D1DAACAP0spPkMAAAAASyk+QwAAgD+jYT5DAAAAAKNhPkMAAIA/+5k+QwAAAAD7mT5DAACAP1PSPkMAAAAAU9I+QwAAgD+rCj9DAAAAAKsKP0MAAIA/A0M/QwAAAAADQz9DAACAP1t7P0MAAAAAW3s/QwAAgD+zsz9DAAAAALOzP0MAAIA/Cuw/QwAAAAAK7D9DAACAP2IkQEMAAAAAYiRAQwAAgD+6XEBDAAAAALpcQEMAAIA/EpVAQwAAAAASlUBDAACAP2rNQEMAAAAAas1AQwAAgD/CBUFDAAAAAMIFQUMAAIA/Gj5BQwAAAAAaPkFDAACAP3F2QUMAAAAAcXZBQwAAgD/JrkFDAAAAAMmuQUMAAIA/IedBQwAAAAAh50FDAACAP3kfQkMAAAAAeR9CQwAAgD/RV0JDAAAAANFXQkMAAIA/KZBCQwAAAAApkEJDAACAP4HIQkMAAAAAgchCQwAAgD/ZAENDAAAAANkAQ0MAAIA/LzlDQwAAAAAvOUNDAACAP4dxQ0MAAAAAh3FDQwAAgD/fqUNDAAAAAN+pQ0MAAIA/N+JDQwAAAAA34kNDAACAP48aREMAAAAAjxpEQwAAgD/nUkRDAAAAAOdSREMAAIA/P4tEQwAAAAA/i0RDAACAP5HDREMAAAAAkcNEQwAAgD/p+0RDAAAAAOn7REMAAIA/QTRFQwAAAABBNEVDAACAP5lsRUMAAAAAmWxFQwAAgD8ipEVDAAAAACKkRUMAAIA/etxFQwAAAAB63EVDAACAP9IURkMAAAAA0hRGQwAAgD8/SkZDAAAAAD9KRkMAAIA/l4JGQwAAAACXgkZDAACAP3i4RkMAAAAAeLhGQwAAgD/Q8EZDAAAAANDwRkMAAIA/"), +"aabb": AABB(-27.86245, -0.432, -26.431866, 54.402885, 1e-05, 51.728455), +"attribute_data": PackedByteArray("AAAAAAAAAAAAAAAAAACAP0HfIz4AAAAAQd8jPgAAgD8S36M+AAAAABLfoz4AAIA/s871PgAAAACzzvU+AACAPyrfIz8AAAAAKt8jPwAAgD/61kw/AAAAAPrWTD8AAIA/s851PwAAAACzznU/AACAP0Jjjz8AAAAAQmOPPwAAgD/F3qM/AAAAAMXeoz8AAIA/uFq4PwAAAAC4Wrg/AACAP5TWzD8AAAAAlNbMPwAAgD97UuE/AAAAAHtS4T8AAIA/Y871PwAAAABjzvU/AACAPyUlBUAAAAAAJSUFQAAAgD8CYw9AAAAAAAJjD0AAAIA/+aAZQAAAAAD5oBlAAACAP+TeI0AAAAAA5N4jQAAAgD/bHC5AAAAAANscLkAAAIA/0Vo4QAAAAADRWjhAAACAP72YQkAAAAAAvZhCQAAAgD+z1kxAAAAAALPWTEAAAIA/PBRXQAAAAAA8FFdAAACAPytSYUAAAAAAK1JhQAAAgD8ekGtAAAAAAB6Qa0AAAIA/Dc51QAAAAAANznVAAACAPwEGgEAAAAAAAQaAQAAAgD/4JIVAAAAAAPgkhUAAAIA/tEOKQAAAAAC0Q4pAAACAP65ij0AAAAAArmKPQAAAgD+mgZRAAAAAAKaBlEAAAIA/oaCZQAAAAAChoJlAAACAP5i/nkAAAAAAmL+eQAAAgD9K3qNAAAAAAEreo0AAAIA/Q/2oQAAAAABD/ahAAACAPzscrkAAAAAAOxyuQAAAgD83O7NAAAAAADc7s0AAAIA/L1q4QAAAAAAvWrhAAACAPxh5vUAAAAAAGHm9QAAAgD8QmMJAAAAAABCYwkAAAIA/C7fHQAAAAAALt8dAAACAPwPWzEAAAAAAA9bMQAAAgD/99NFAAAAAAP300UAAAIA/ahPXQAAAAABqE9dAAACAP2My3EAAAAAAYzLcQAAAgD9cUeFAAAAAAFxR4UAAAIA/VnDmQAAAAABWcOZAAACAP06P60AAAAAATo/rQAAAgD+CrfBAAAAAAIKt8EAAAIA/e8z1QAAAAAB7zPVAAACAP3Tr+kAAAAAAdOv6QAAAgD83BQBBAAAAADcFAEEAAIA/jpQCQQAAAACOlAJBAACAPwskBUEAAAAACyQFQQAAgD+IswdBAAAAAIizB0EAAIA/BUMKQQAAAAAFQwpBAACAP17SDEEAAAAAXtIMQQAAgD/aYQ9BAAAAANphD0EAAIA/WPERQQAAAABY8RFBAACAP9SAFEEAAAAA1IAUQQAAgD9FEBdBAAAAAEUQF0EAAIA/wZ8ZQQAAAADBnxlBAACAPz8vHEEAAAAAPy8cQQAAgD+7vh5BAAAAALu+HkEAAIA/OE4hQQAAAAA4TiFBAACAP5vdI0EAAAAAm90jQQAAgD8XbSZBAAAAABdtJkEAAIA/lPwoQQAAAACU/ChBAACAPxCMK0EAAAAAEIwrQQAAgD97Gy5BAAAAAHsbLkEAAIA/+KowQQAAAAD4qjBBAACAP3Q6M0EAAAAAdDozQQAAgD/xyTVBAAAAAPHJNUEAAIA/bVk4QQAAAABtWThBAACAP9LoOkEAAAAA0ug6QQAAgD9PeD1BAAAAAE94PUEAAIA/ywdAQQAAAADLB0BBAACAP0iXQkEAAAAASJdCQQAAgD/FJkVBAAAAAMUmRUEAAIA/N7ZHQQAAAAA3tkdBAACAP7RFSkEAAAAAtEVKQQAAgD8w1UxBAAAAADDVTEEAAIA/rWRPQQAAAACtZE9BAACAPyb0UUEAAAAAJvRRQQAAgD+jg1RBAAAAAKODVEEAAIA/IBNXQQAAAAAgE1dBAACAP5yiWUEAAAAAnKJZQQAAgD8ZMlxBAAAAABkyXEEAAIA/ksFeQQAAAACSwV5BAACAPw5RYUEAAAAADlFhQQAAgD+L4GNBAAAAAIvgY0EAAIA/CHBmQQAAAAAIcGZBAACAP4T/aEEAAAAAhP9oQQAAgD/3jmtBAAAAAPeOa0EAAIA/dB5uQQAAAAB0Hm5BAACAP/GtcEEAAAAA8a1wQQAAgD9tPXNBAAAAAG09c0EAAIA/6sx1QQAAAADqzHVBAACAP1hceEEAAAAAWFx4QQAAgD/V63pBAAAAANXrekEAAIA/Unt9QQAAAABSe31BAACAP2cFgEEAAAAAZwWAQQAAgD8lTYFBAAAAACVNgUEAAIA/4JSCQQAAAADglIJBAACAP57cg0EAAAAAntyDQQAAgD9cJIVBAAAAAFwkhUEAAIA/G2yGQQAAAAAbbIZBAACAP9Kzh0EAAAAA0rOHQQAAgD+Q+4hBAAAAAJD7iEEAAIA/TkOKQQAAAABOQ4pBAACAPw2Li0EAAAAADYuLQQAAgD/L0oxBAAAAAMvSjEEAAIA/hBqOQQAAAACEGo5BAACAP0Nij0EAAAAAQ2KPQQAAgD8BqpBBAAAAAAGqkEEAAIA/v/GRQQAAAAC/8ZFBAACAP3Q5k0EAAAAAdDmTQQAAgD8ygZRBAAAAADKBlEEAAIA/8MiVQQAAAADwyJVBAACAP68Ql0EAAAAArxCXQQAAgD9nWJhBAAAAAGdYmEEAAIA/JaCZQQAAAAAloJlBAACAP+PnmkEAAAAA4+eaQQAAgD+iL5xBAAAAAKIvnEEAAIA/YHedQQAAAABgd51BAACAPx6/nkEAAAAAHr+eQQAAgD/dBqBBAAAAAN0GoEEAAIA/m06hQQAAAACbTqFBAACAP1mWokEAAAAAWZaiQQAAgD8Y3qNBAAAAABjeo0EAAIA/1iWlQQAAAADWJaVBAACAP5RtpkEAAAAAlG2mQQAAgD9TtadBAAAAAFO1p0EAAIA/Ef2oQQAAAAAR/ahBAACAP89EqkEAAAAAz0SqQQAAgD+OjKtBAAAAAI6Mq0EAAIA/TNSsQQAAAABM1KxBAACAPwocrkEAAAAAChyuQQAAgD/JY69BAAAAAMljr0EAAIA//KqwQQAAAAD8qrBBAACAP7nxsUEAAAAAufGxQQAAgD93ObNBAAAAAHc5s0EAAIA/NYG0QQAAAAA1gbRBAACAP/TItUEAAAAA9Mi1QQAAgD+yELdBAAAAALIQt0EAAIA/cFi4QQAAAABwWLhBAACAPy+guUEAAAAAL6C5QQAAgD/t57pBAAAAAO3nukEAAIA/qy+8QQAAAACrL7xBAACAP2l3vUEAAAAAaXe9QQAAgD8ov75BAAAAACi/vkEAAIA/5gbAQQAAAADmBsBBAACAP6ROwUEAAAAApE7BQQAAgD9ilsJBAAAAAGKWwkEAAIA/F97DQQAAAAAX3sNBAACAP8slxUEAAAAAyyXFQQAAgD98bcZBAAAAAHxtxkEAAIA/LbXHQQAAAAAttcdBAACAP9z8yEEAAAAA3PzIQQAAgD+JRMpBAAAAAIlEykEAAIA/OIzLQQAAAAA4jMtBAACAP+fTzEEAAAAA59PMQQAAgD+UG85BAAAAAJQbzkEAAIA/QWPPQQAAAABBY89BAACAP/Kq0EEAAAAA8qrQQQAAgD+j8tFBAAAAAKPy0UEAAIA/VTrTQQAAAABVOtNBAACAPwiC1EEAAAAACILUQQAAgD/CydVBAAAAAMLJ1UEAAIA/gBHXQQAAAACAEddBAACAPz5Z2EEAAAAAPlnYQQAAgD/9oNlBAAAAAP2g2UEAAIA/u+jaQQAAAAC76NpBAACAP3kw3EEAAAAAeTDcQQAAgD83eN1BAAAAADd43UEAAIA/9r/eQQAAAAD2v95BAACAP7QH4EEAAAAAtAfgQQAAgD9vT+FBAAAAAG9P4UEAAIA/LZfiQQAAAAAtl+JBAACAP+fe40EAAAAA597jQQAAgD+lJuVBAAAAAKUm5UEAAIA/XW7mQQAAAABdbuZBAACAPxu250EAAAAAG7bnQQAAgD/Z/ehBAAAAANn96EEAAIA/lkXqQQAAAACWRepBAACAP1SN60EAAAAAVI3rQQAAgD8S1exBAAAAABLV7EEAAIA/0BzuQQAAAADQHO5BAACAP45k70EAAAAAjmTvQQAAgD9NrPBBAAAAAE2s8EEAAIA/CfTxQQAAAAAJ9PFBAACAP8g780EAAAAAyDvzQQAAgD+Gg/RBAAAAAIaD9EEAAIA/RMv1QQAAAABEy/VBAACAPwIT90EAAAAAAhP3QQAAgD/BWvhBAAAAAMFa+EEAAIA/f6L5QQAAAAB/ovlBAACAPzzq+kEAAAAAPOr6QQAAgD/7MfxBAAAAAPsx/EEAAIA/uXn9QQAAAAC5ef1BAACAP3fB/kEAAAAAd8H+QQAAgD+YBABCAAAAAJgEAEIAAIA/d6gAQgAAAAB3qABCAACAP1ZMAUIAAAAAVkwBQgAAgD828AFCAAAAADbwAUIAAIA/EpQCQgAAAAASlAJCAACAP/E3A0IAAAAA8TcDQgAAgD/Q2wNCAAAAANDbA0IAAIA/r38EQgAAAACvfwRCAACAP4kjBUIAAAAAiSMFQgAAgD9oxwVCAAAAAGjHBUIAAIA/R2sGQgAAAABHawZCAACAPyYPB0IAAAAAJg8HQgAAgD//sgdCAAAAAP+yB0IAAIA/3lYIQgAAAADeVghCAACAP736CEIAAAAAvfoIQgAAgD+bnglCAAAAAJueCUIAAIA/e0IKQgAAAAB7QgpCAACAP1rmCkIAAAAAWuYKQgAAgD85igtCAAAAADmKC0IAAIA/Dy4MQgAAAAAPLgxCAACAP+7RDEIAAAAA7tEMQgAAgD/NdQ1CAAAAAM11DUIAAIA/pBkOQgAAAACkGQ5CAACAP4O9DkIAAAAAg70OQgAAgD9jYQ9CAAAAAGNhD0IAAIA/PwUQQgAAAAA/BRBCAACAPx6pEEIAAAAAHqkQQgAAgD/9TBFCAAAAAP1MEUIAAIA/3fARQgAAAADd8BFCAACAP7yUEkIAAAAAvJQSQgAAgD+bOBNCAAAAAJs4E0IAAIA/etwTQgAAAAB63BNCAACAP1mAFEIAAAAAWYAUQgAAgD84JBVCAAAAADgkFUIAAIA/GMgVQgAAAAAYyBVCAACAP/drFkIAAAAA92sWQgAAgD/WDxdCAAAAANYPF0IAAIA/tbMXQgAAAAC1sxdCAACAP5RXGEIAAAAAlFcYQgAAgD9z+xhCAAAAAHP7GEIAAIA/UZ8ZQgAAAABRnxlCAACAPypDGkIAAAAAKkMaQgAAgD8C5xpCAAAAAALnGkIAAIA/2oobQgAAAADaihtCAACAP7EuHEIAAAAAsS4cQgAAgD+H0hxCAAAAAIfSHEIAAIA/XXYdQgAAAABddh1CAACAPzMaHkIAAAAAMxoeQgAAgD8Jvh5CAAAAAAm+HkIAAIA/32EfQgAAAADfYR9CAACAP7cFIEIAAAAAtwUgQgAAgD+PqSBCAAAAAI+pIEIAAIA/aE0hQgAAAABoTSFCAACAP0PxIUIAAAAAQ/EhQgAAgD8ilSJCAAAAACKVIkIAAIA/ATkjQgAAAAABOSNCAACAP+DcI0IAAAAA4NwjQgAAgD+/gCRCAAAAAL+AJEIAAIA/niQlQgAAAACeJCVCAACAP33IJUIAAAAAfcglQgAAgD9cbCZCAAAAAFxsJkIAAIA/PBAnQgAAAAA8ECdCAACAPxu0J0IAAAAAG7QnQgAAgD/6VyhCAAAAAPpXKEIAAIA/2fsoQgAAAADZ+yhCAACAP7ifKUIAAAAAuJ8pQgAAgD+YQypCAAAAAJhDKkIAAIA/d+cqQgAAAAB35ypCAACAP1aLK0IAAAAAVosrQgAAgD81LyxCAAAAADUvLEIAAIA/FNMsQgAAAAAU0yxCAACAP/N2LUIAAAAA83YtQgAAgD/SGi5CAAAAANIaLkIAAIA/Tb0uQgAAAABNvS5CAACAPy1hL0IAAAAALWEvQgAAgD8MBTBCAAAAAAwFMEIAAIA/66gwQgAAAADrqDBCAACAP8pMMUIAAAAAykwxQgAAgD+p8DFCAAAAAKnwMUIAAIA/iJQyQgAAAACIlDJCAACAP2g4M0IAAAAAaDgzQgAAgD9H3DNCAAAAAEfcM0IAAIA/JoA0QgAAAAAmgDRCAACAPwUkNUIAAAAABSQ1QgAAgD/kxzVCAAAAAOTHNUIAAIA/w2s2QgAAAADDazZCAACAP6IPN0IAAAAAog83QgAAgD+CszdCAAAAAIKzN0IAAIA/TVc4QgAAAABNVzhCAACAPyz7OEIAAAAALPs4QgAAgD8MnzlCAAAAAAyfOUIAAIA/5EI6QgAAAADkQjpCAACAP8TmOkIAAAAAxOY6QgAAgD+jijtCAAAAAKOKO0IAAIA/gi48QgAAAACCLjxCAACAP1XSPEIAAAAAVdI8QgAAgD80dj1CAAAAADR2PUIAAIA/FBo+QgAAAAAUGj5CAACAP/O9PkIAAAAA870+QgAAgD/JYT9CAAAAAMlhP0IAAIA/qAVAQgAAAACoBUBCAACAP4epQEIAAAAAh6lAQgAAgD9mTUFCAAAAAGZNQUIAAIA/P/FBQgAAAAA/8UFCAACAPx6VQkIAAAAAHpVCQgAAgD/9OENCAAAAAP04Q0IAAIA/3NxDQgAAAADc3ENCAACAP7yAREIAAAAAvIBEQgAAgD+ZJEVCAAAAAJkkRUIAAIA/eMhFQgAAAAB4yEVCAACAP1dsRkIAAAAAV2xGQgAAgD82EEdCAAAAADYQR0IAAIA/ErRHQgAAAAAStEdCAACAP/FXSEIAAAAA8VdIQgAAgD/R+0hCAAAAANH7SEIAAIA/sJ9JQgAAAACwn0lCAACAP49DSkIAAAAAj0NKQgAAgD9r50pCAAAAAGvnSkIAAIA/SotLQgAAAABKi0tCAACAPyovTEIAAAAAKi9MQgAAgD8J00xCAAAAAAnTTEIAAIA/6HZNQgAAAADodk1CAACAP8caTkIAAAAAxxpOQgAAgD+mvk5CAAAAAKa+TkIAAIA/hWJPQgAAAACFYk9CAACAP2UGUEIAAAAAZQZQQgAAgD9EqlBCAAAAAESqUEIAAIA/I05RQgAAAAAjTlFCAACAPwLyUUIAAAAAAvJRQgAAgD/hlVJCAAAAAOGVUkIAAIA/wDlTQgAAAADAOVNCAACAP6DdU0IAAAAAoN1TQgAAgD9/gVRCAAAAAH+BVEIAAIA/XiVVQgAAAABeJVVCAACAPz3JVUIAAAAAPclVQgAAgD8cbVZCAAAAABxtVkIAAIA/+xBXQgAAAAD7EFdCAACAP9q0V0IAAAAA2rRXQgAAgD+6WFhCAAAAALpYWEIAAIA/mfxYQgAAAACZ/FhCAACAP3igWUIAAAAAeKBZQgAAgD9BRFpCAAAAAEFEWkIAAIA/8OdaQgAAAADw51pCAACAP5uLW0IAAAAAm4tbQgAAgD8ZL1xCAAAAABkvXEIAAIA/+NJcQgAAAAD40lxCAACAP9h2XUIAAAAA2HZdQgAAgD+3Gl5CAAAAALcaXkIAAIA/lr5eQgAAAACWvl5CAACAP3ViX0IAAAAAdWJfQgAAgD9UBmBCAAAAAFQGYEIAAIA/M6pgQgAAAAAzqmBCAACAPxNOYUIAAAAAE05hQgAAgD/y8WFCAAAAAPLxYUIAAIA/0ZViQgAAAADRlWJCAACAP7A5Y0IAAAAAsDljQgAAgD+P3WNCAAAAAI/dY0IAAIA/boFkQgAAAABugWRCAACAP04lZUIAAAAATiVlQgAAgD8tyWVCAAAAAC3JZUIAAIA/DG1mQgAAAAAMbWZCAACAP+sQZ0IAAAAA6xBnQgAAgD/KtGdCAAAAAMq0Z0IAAIA/qVhoQgAAAACpWGhCAACAP4n8aEIAAAAAifxoQgAAgD9ooGlCAAAAAGigaUIAAIA/R0RqQgAAAABHRGpCAACAPyboakIAAAAAJuhqQgAAgD8FjGtCAAAAAAWMa0IAAIA/5C9sQgAAAADkL2xCAACAP8TTbEIAAAAAxNNsQgAAgD+jd21CAAAAAKN3bUIAAIA/ghtuQgAAAACCG25CAACAP2G/bkIAAAAAYb9uQgAAgD9AY29CAAAAAEBjb0IAAIA/HwdwQgAAAAAfB3BCAACAP/+qcEIAAAAA/6pwQgAAgD/eTnFCAAAAAN5OcUIAAIA/vfJxQgAAAAC98nFCAACAP5yWckIAAAAAnJZyQgAAgD97OnNCAAAAAHs6c0IAAIA/Wt5zQgAAAABa3nNCAACAPzqCdEIAAAAAOoJ0QgAAgD8ZJnVCAAAAABkmdUIAAIA/+Ml1QgAAAAD4yXVCAACAP9dtdkIAAAAA1212QgAAgD+2EXdCAAAAALYRd0IAAIA/lbV3QgAAAACVtXdCAACAP3VZeEIAAAAAdVl4QgAAgD9U/XhCAAAAAFT9eEIAAIA/M6F5QgAAAAAzoXlCAACAPxJFekIAAAAAEkV6QgAAgD/x6HpCAAAAAPHoekIAAIA/0Ix7QgAAAADQjHtCAACAP7AwfEIAAAAAsDB8QgAAgD+P1HxCAAAAAI/UfEIAAIA/bnh9QgAAAABueH1CAACAP00cfkIAAAAATRx+QgAAgD8swH5CAAAAACzAfkIAAIA/DGR/QgAAAAAMZH9CAACAP/UDgEIAAAAA9QOAQgAAgD/lVYBCAAAAAOVVgEIAAIA/1KeAQgAAAADUp4BCAACAP8T5gEIAAAAAxPmAQgAAgD+0S4FCAAAAALRLgUIAAIA/o52BQgAAAACjnYFCAACAP5PvgUIAAAAAk++BQgAAgD+CQYJCAAAAAIJBgkIAAIA/cpOCQgAAAAByk4JCAACAP2LlgkIAAAAAYuWCQgAAgD9RN4NCAAAAAFE3g0IAAIA/QYmDQgAAAABBiYNCAACAPzDbg0IAAAAAMNuDQgAAgD8gLYRCAAAAACAthEIAAIA/D3+EQgAAAAAPf4RCAACAP//QhEIAAAAA/9CEQgAAgD/vIoVCAAAAAO8ihUIAAIA/3nSFQgAAAADedIVCAACAP87GhUIAAAAAzsaFQgAAgD+9GIZCAAAAAL0YhkIAAIA/rWqGQgAAAACtaoZCAACAP528hkIAAAAAnbyGQgAAgD+MDodCAAAAAIwOh0IAAIA/fGCHQgAAAAB8YIdCAACAP2uyh0IAAAAAa7KHQgAAgD9bBIhCAAAAAFsEiEIAAIA/S1aIQgAAAABLVohCAACAPzqoiEIAAAAAOqiIQgAAgD8q+ohCAAAAACr6iEIAAIA/GUyJQgAAAAAZTIlCAACAPwmeiUIAAAAACZ6JQgAAgD/474lCAAAAAPjviUIAAIA/6EGKQgAAAADoQYpCAACAP9iTikIAAAAA2JOKQgAAgD/H5YpCAAAAAMflikIAAIA/tzeLQgAAAAC3N4tCAACAP6aJi0IAAAAApomLQgAAgD+W24tCAAAAAJbbi0IAAIA/hi2MQgAAAACGLYxCAACAP3V/jEIAAAAAdX+MQgAAgD9l0YxCAAAAAGXRjEIAAIA/VCONQgAAAABUI41CAACAP0R1jUIAAAAARHWNQgAAgD8zx41CAAAAADPHjUIAAIA/IxmOQgAAAAAjGY5CAACAPxNrjkIAAAAAE2uOQgAAgD8CvY5CAAAAAAK9jkIAAIA/8g6PQgAAAADyDo9CAACAP+Fgj0IAAAAA4WCPQgAAgD/Rso9CAAAAANGyj0IAAIA/wASQQgAAAADABJBCAACAP7BWkEIAAAAAsFaQQgAAgD+gqJBCAAAAAKCokEIAAIA/j/qQQgAAAACP+pBCAACAP39MkUIAAAAAf0yRQgAAgD9vnpFCAAAAAG+ekUIAAIA/XvCRQgAAAABe8JFCAACAP05CkkIAAAAATkKSQgAAgD89lJJCAAAAAD2UkkIAAIA/LeaSQgAAAAAt5pJCAACAPxw4k0IAAAAAHDiTQgAAgD8MipNCAAAAAAyKk0IAAIA//NuTQgAAAAD825NCAACAP+stlEIAAAAA6y2UQgAAgD/bf5RCAAAAANt/lEIAAIA/ytGUQgAAAADK0ZRCAACAP7ojlUIAAAAAuiOVQgAAgD+pdZVCAAAAAKl1lUIAAIA/mceVQgAAAACZx5VCAACAP4kZlkIAAAAAiRmWQgAAgD94a5ZCAAAAAHhrlkIAAIA/aL2WQgAAAABovZZCAACAP1cPl0IAAAAAVw+XQgAAgD9HYZdCAAAAAEdhl0IAAIA/N7OXQgAAAAA3s5dCAACAPyYFmEIAAAAAJgWYQgAAgD8WV5hCAAAAABZXmEIAAIA/BamYQgAAAAAFqZhCAACAP/X6mEIAAAAA9fqYQgAAgD/kTJlCAAAAAORMmUIAAIA/1J6ZQgAAAADUnplCAACAP8TwmUIAAAAAxPCZQgAAgD+zQppCAAAAALNCmkIAAIA/o5SaQgAAAACjlJpCAACAP5LmmkIAAAAAkuaaQgAAgD+COJtCAAAAAII4m0IAAIA/coqbQgAAAAByiptCAACAP2Hcm0IAAAAAYdybQgAAgD9RLpxCAAAAAFEunEIAAIA/M4CcQgAAAAAzgJxCAACAPx7SnEIAAAAAHtKcQgAAgD8JJJ1CAAAAAAkknUIAAIA/83WdQgAAAADzdZ1CAACAP93HnUIAAAAA3cedQgAAgD/HGZ5CAAAAAMcZnkIAAIA/sWueQgAAAACxa55CAACAP5y9nkIAAAAAnL2eQgAAgD+GD59CAAAAAIYPn0IAAIA/cWGfQgAAAABxYZ9CAACAP2Gzn0IAAAAAYbOfQgAAgD9QBaBCAAAAAFAFoEIAAIA/QFegQgAAAABAV6BCAACAPzCpoEIAAAAAMKmgQgAAgD8f+6BCAAAAAB/7oEIAAIA/D02hQgAAAAAPTaFCAACAP/6eoUIAAAAA/p6hQgAAgD/u8KFCAAAAAO7woUIAAIA/3kKiQgAAAADeQqJCAACAP82UokIAAAAAzZSiQgAAgD+95qJCAAAAAL3mokIAAIA/rDijQgAAAACsOKNCAACAP5yKo0IAAAAAnIqjQgAAgD+M3KNCAAAAAIzco0IAAIA/ey6kQgAAAAB7LqRCAACAP2uApEIAAAAAa4CkQgAAgD9a0qRCAAAAAFrSpEIAAIA/SiSlQgAAAABKJKVCAACAPzl2pUIAAAAAOXalQgAAgD8pyKVCAAAAACnIpUIAAIA/GBqmQgAAAAAYGqZCAACAPwhspkIAAAAACGymQgAAgD/4vaZCAAAAAPi9pkIAAIA/5w+nQgAAAADnD6dCAACAP9dhp0IAAAAA12GnQgAAgD/Gs6dCAAAAAMazp0IAAIA/tgWoQgAAAAC2BahCAACAP6ZXqEIAAAAApleoQgAAgD+VqahCAAAAAJWpqEIAAIA/hfuoQgAAAACF+6hCAACAP3RNqUIAAAAAdE2pQgAAgD9kn6lCAAAAAGSfqUIAAIA/VPGpQgAAAABU8alCAACAP0NDqkIAAAAAQ0OqQgAAgD8zlapCAAAAADOVqkIAAIA/IueqQgAAAAAi56pCAACAPxI5q0IAAAAAEjmrQgAAgD8Bi6tCAAAAAAGLq0IAAIA/8dyrQgAAAADx3KtCAACAP+EurEIAAAAA4S6sQgAAgD/QgKxCAAAAANCArEIAAIA/wNKsQgAAAADA0qxCAACAP68krUIAAAAAryStQgAAgD+fdq1CAAAAAJ92rUIAAIA/j8itQgAAAACPyK1CAACAP34arkIAAAAAfhquQgAAgD9ubK5CAAAAAG5srkIAAIA/Xb6uQgAAAABdvq5CAACAP00Qr0IAAAAATRCvQgAAgD89Yq9CAAAAAD1ir0IAAIA/LLSvQgAAAAAstK9CAACAPxwGsEIAAAAAHAawQgAAgD8LWLBCAAAAAAtYsEIAAIA/+6mwQgAAAAD7qbBCAACAP+r7sEIAAAAA6vuwQgAAgD/aTbFCAAAAANpNsUIAAIA/yp+xQgAAAADKn7FCAACAP7nxsUIAAAAAufGxQgAAgD+pQ7JCAAAAAKlDskIAAIA/mJWyQgAAAACYlbJCAACAP4jnskIAAAAAiOeyQgAAgD93ObNCAAAAAHc5s0IAAIA/Z4uzQgAAAABni7NCAACAP1fds0IAAAAAV92zQgAAgD9GL7RCAAAAAEYvtEIAAIA/NoG0QgAAAAA2gbRCAACAPyXTtEIAAAAAJdO0QgAAgD8VJbVCAAAAABUltUIAAIA/BXe1QgAAAAAFd7VCAACAP/TItUIAAAAA9Mi1QgAAgD/kGrZCAAAAAOQatkIAAIA/02y2QgAAAADTbLZCAACAP8O+tkIAAAAAw762QgAAgD+yELdCAAAAALIQt0IAAIA/f2K3QgAAAAB/YrdCAACAP2K0t0IAAAAAYrS3QgAAgD9DBrhCAAAAAEMGuEIAAIA/Jli4QgAAAAAmWLhCAACAP/WpuEIAAAAA9am4QgAAgD/l+7hCAAAAAOX7uEIAAIA/1E25QgAAAADUTblCAACAP8SfuUIAAAAAxJ+5QgAAgD+z8blCAAAAALPxuUIAAIA/o0O6QgAAAACjQ7pCAACAP5OVukIAAAAAk5W6QgAAgD+C57pCAAAAAILnukIAAIA/cjm7QgAAAAByObtCAACAP2GLu0IAAAAAYYu7QgAAgD9R3btCAAAAAFHdu0IAAIA/QC+8QgAAAABAL7xCAACAPzCBvEIAAAAAMIG8QgAAgD8f07xCAAAAAB/TvEIAAIA/DyW9QgAAAAAPJb1CAACAP/92vUIAAAAA/3a9QgAAgD/uyL1CAAAAAO7IvUIAAIA/3hq+QgAAAADeGr5CAACAP81svkIAAAAAzWy+QgAAgD+9vr5CAAAAAL2+vkIAAIA/rRC/QgAAAACtEL9CAACAP5xiv0IAAAAAnGK/QgAAgD+MtL9CAAAAAIy0v0IAAIA/ewbAQgAAAAB7BsBCAACAP2tYwEIAAAAAa1jAQgAAgD9bqsBCAAAAAFuqwEIAAIA/SvzAQgAAAABK/MBCAACAPzpOwUIAAAAAOk7BQgAAgD8poMFCAAAAACmgwUIAAIA/GfLBQgAAAAAZ8sFCAACAPwhEwkIAAAAACETCQgAAgD/4lcJCAAAAAPiVwkIAAIA/6OfCQgAAAADo58JCAACAP9c5w0IAAAAA1znDQgAAgD/Hi8NCAAAAAMeLw0IAAIA/tt3DQgAAAAC23cNCAACAP6YvxEIAAAAApi/EQgAAgD+VgcRCAAAAAJWBxEIAAIA/hdPEQgAAAACF08RCAACAP3UlxUIAAAAAdSXFQgAAgD9kd8VCAAAAAGR3xUIAAIA/VMnFQgAAAABUycVCAACAP0MbxkIAAAAAQxvGQgAAgD8ybcZCAAAAADJtxkIAAIA/Ir/GQgAAAAAiv8ZCAACAPxERx0IAAAAAERHHQgAAgD8BY8dCAAAAAAFjx0IAAIA/8LTHQgAAAADwtMdCAACAP+AGyEIAAAAA4AbIQgAAgD/QWMhCAAAAANBYyEIAAIA/v6rIQgAAAAC/qshCAACAP6/8yEIAAAAAr/zIQgAAgD+eTslCAAAAAJ5OyUIAAIA/jqDJQgAAAACOoMlCAACAP37yyUIAAAAAfvLJQgAAgD9sRMpCAAAAAGxEykIAAIA/W5bKQgAAAABblspCAACAP0voykIAAAAAS+jKQgAAgD86OstCAAAAADo6y0IAAIA/KozLQgAAAAAqjMtCAACAPxney0IAAAAAGd7LQgAAgD8IMMxCAAAAAAgwzEIAAIA/94HMQgAAAAD3gcxCAACAP+fTzEIAAAAA59PMQgAAgD/XJc1CAAAAANclzUIAAIA/xnfNQgAAAADGd81CAACAP7bJzUIAAAAAtsnNQgAAgD+kG85CAAAAAKQbzkIAAIA/k23OQgAAAACTbc5CAACAP4O/zkIAAAAAg7/OQgAAgD9yEc9CAAAAAHIRz0IAAIA/YmPPQgAAAABiY89CAACAP1G1z0IAAAAAUbXPQgAAgD8+B9BCAAAAAD4H0EIAAIA/LlnQQgAAAAAuWdBCAACAPx2r0EIAAAAAHavQQgAAgD8N/dBCAAAAAA390EIAAIA//U7RQgAAAAD9TtFCAACAP+qg0UIAAAAA6qDRQgAAgD/a8tFCAAAAANry0UIAAIA/yUTSQgAAAADJRNJCAACAP7mW0kIAAAAAuZbSQgAAgD+o6NJCAAAAAKjo0kIAAIA/ljrTQgAAAACWOtNCAACAP4aM00IAAAAAhozTQgAAgD913tNCAAAAAHXe00IAAIA/ZTDUQgAAAABlMNRCAACAP1WC1EIAAAAAVYLUQgAAgD9E1NRCAAAAAETU1EIAAIA/MSbVQgAAAAAxJtVCAACAPyF41UIAAAAAIXjVQgAAgD8QytVCAAAAABDK1UIAAIA/ABzWQgAAAAAAHNZCAACAP/Bt1kIAAAAA8G3WQgAAgD/cv9ZCAAAAANy/1kIAAIA/yxHXQgAAAADLEddCAACAP7tj10IAAAAAu2PXQgAAgD+qtddCAAAAAKq110IAAIA/mgfYQgAAAACaB9hCAACAP4pZ2EIAAAAAilnYQgAAgD93q9hCAAAAAHer2EIAAIA/Zv3YQgAAAABm/dhCAACAP1ZP2UIAAAAAVk/ZQgAAgD9FodlCAAAAAEWh2UIAAIA/NfPZQgAAAAA189lCAACAPyVF2kIAAAAAJUXaQgAAgD8Tl9pCAAAAABOX2kIAAIA/A+naQgAAAAAD6dpCAACAP/I620IAAAAA8jrbQgAAgD/ijNtCAAAAAOKM20IAAIA/0t7bQgAAAADS3ttCAACAP8Ew3EIAAAAAwTDcQgAAgD+wgtxCAAAAALCC3EIAAIA/oNTcQgAAAACg1NxCAACAP48m3UIAAAAAjybdQgAAgD9/eN1CAAAAAH943UIAAIA/bsrdQgAAAABuyt1CAACAP14c3kIAAAAAXhzeQgAAgD9Mbt5CAAAAAExu3kIAAIA/O8DeQgAAAAA7wN5CAACAPysS30IAAAAAKxLfQgAAgD8bZN9CAAAAABtk30IAAIA/CrbfQgAAAAAKtt9CAACAP/oH4EIAAAAA+gfgQgAAgD/oWeBCAAAAAOhZ4EIAAIA/16vgQgAAAADXq+BCAACAP8f94EIAAAAAx/3gQgAAgD+2T+FCAAAAALZP4UIAAIA/pqHhQgAAAACmoeFCAACAP5bz4UIAAAAAlvPhQgAAgD+FReJCAAAAAIVF4kIAAIA/dZfiQgAAAAB1l+JCAACAP2Tp4kIAAAAAZOniQgAAgD9UO+NCAAAAAFQ740IAAIA/Q43jQgAAAABDjeNCAACAPzPf40IAAAAAM9/jQgAAgD8jMeRCAAAAACMx5EIAAIA/EoPkQgAAAAASg+RCAACAPwLV5EIAAAAAAtXkQgAAgD/xJuVCAAAAAPEm5UIAAIA/4XjlQgAAAADheOVCAACAP9HK5UIAAAAA0crlQgAAgD/AHOZCAAAAAMAc5kIAAIA/sG7mQgAAAACwbuZCAACAP5/A5kIAAAAAn8DmQgAAgD+PEudCAAAAAI8S50IAAIA/fmTnQgAAAAB+ZOdCAACAP26250IAAAAAbrbnQgAAgD9eCOhCAAAAAF4I6EIAAIA/TVroQgAAAABNWuhCAACAPz2s6EIAAAAAPazoQgAAgD8s/uhCAAAAACz+6EIAAIA/HFDpQgAAAAAcUOlCAACAPwyi6UIAAAAADKLpQgAAgD/78+lCAAAAAPvz6UIAAIA/5kXqQgAAAADmRepCAACAP9SX6kIAAAAA1JfqQgAAgD/B6epCAAAAAMHp6kIAAIA/rzvrQgAAAACvO+tCAACAP5yN60IAAAAAnI3rQgAAgD+J3+tCAAAAAInf60IAAIA/djHsQgAAAAB2MexCAACAP2KD7EIAAAAAYoPsQgAAgD9O1exCAAAAAE7V7EIAAIA/OiftQgAAAAA6J+1CAACAPyd57UIAAAAAJ3ntQgAAgD8Ty+1CAAAAABPL7UIAAIA/AB3uQgAAAAAAHe5CAACAP+xu7kIAAAAA7G7uQgAAgD/ZwO5CAAAAANnA7kIAAIA/xhLvQgAAAADGEu9CAACAP7Rk70IAAAAAtGTvQgAAgD+htu9CAAAAAKG270IAAIA/jwjwQgAAAACPCPBCAACAP3pa8EIAAAAAelrwQgAAgD9prPBCAAAAAGms8EIAAIA/Wf7wQgAAAABZ/vBCAACAP0hQ8UIAAAAASFDxQgAAgD84ovFCAAAAADii8UIAAIA/KPTxQgAAAAAo9PFCAACAPxdG8kIAAAAAF0byQgAAgD8HmPJCAAAAAAeY8kIAAIA/9unyQgAAAAD26fJCAACAP+Y780IAAAAA5jvzQgAAgD/WjfNCAAAAANaN80IAAIA/xd/zQgAAAADF3/NCAACAP7Ux9EIAAAAAtTH0QgAAgD+kg/RCAAAAAKSD9EIAAIA/lNX0QgAAAACU1fRCAACAP4Qn9UIAAAAAhCf1QgAAgD9zefVCAAAAAHN59UIAAIA/Y8v1QgAAAABjy/VCAACAP1Id9kIAAAAAUh32QgAAgD9Cb/ZCAAAAAEJv9kIAAIA/McH2QgAAAAAxwfZCAACAPyET90IAAAAAIRP3QgAAgD8RZfdCAAAAABFl90IAAIA/ALf3QgAAAAAAt/dCAACAP/AI+EIAAAAA8Aj4QgAAgD/fWvhCAAAAAN9a+EIAAIA/z6z4QgAAAADPrPhCAACAP7/++EIAAAAAv/74QgAAgD+uUPlCAAAAAK5Q+UIAAIA/nqL5QgAAAACeovlCAACAP430+UIAAAAAjfT5QgAAgD98RvpCAAAAAHxG+kIAAIA/bJj6QgAAAABsmPpCAACAP1vq+kIAAAAAW+r6QgAAgD9LPPtCAAAAAEs8+0IAAIA/O477QgAAAAA7jvtCAACAPyrg+0IAAAAAKuD7QgAAgD8ZMvxCAAAAABky/EIAAIA/CYT8QgAAAAAJhPxCAACAP/jV/EIAAAAA+NX8QgAAgD/oJ/1CAAAAAOgn/UIAAIA/13n9QgAAAADXef1CAACAP8fL/UIAAAAAx8v9QgAAgD+2Hf5CAAAAALYd/kIAAIA/pW/+QgAAAAClb/5CAACAP5XB/kIAAAAAlcH+QgAAgD+EE/9CAAAAAIQT/0IAAIA/dGX/QgAAAAB0Zf9CAACAP2O3/0IAAAAAY7f/QgAAgD+pBABDAAAAAKkEAEMAAIA/oS0AQwAAAAChLQBDAACAP5lWAEMAAAAAmVYAQwAAgD+RfwBDAAAAAJF/AEMAAIA/iKgAQwAAAACIqABDAACAP4DRAEMAAAAAgNEAQwAAgD94+gBDAAAAAHj6AEMAAIA/byMBQwAAAABvIwFDAACAP2dMAUMAAAAAZ0wBQwAAgD9edQFDAAAAAF51AUMAAIA/Vp4BQwAAAABWngFDAACAP03HAUMAAAAATccBQwAAgD9F8AFDAAAAAEXwAUMAAIA/OxkCQwAAAAA7GQJDAACAPzNCAkMAAAAAM0ICQwAAgD8rawJDAAAAACtrAkMAAIA/I5QCQwAAAAAjlAJDAACAPxi9AkMAAAAAGL0CQwAAgD8Q5gJDAAAAABDmAkMAAIA/Bw8DQwAAAAAHDwNDAACAP/83A0MAAAAA/zcDQwAAgD/3YANDAAAAAPdgA0MAAIA/74kDQwAAAADviQNDAACAP+eyA0MAAAAA57IDQwAAgD/e2wNDAAAAAN7bA0MAAIA/1gQEQwAAAADWBARDAACAP84tBEMAAAAAzi0EQwAAgD/GVgRDAAAAAMZWBEMAAIA/vX8EQwAAAAC9fwRDAACAP7WoBEMAAAAAtagEQwAAgD+t0QRDAAAAAK3RBEMAAIA/pfoEQwAAAACl+gRDAACAP50jBUMAAAAAnSMFQwAAgD8aTAVDAAAAABpMBUMAAIA/EXUFQwAAAAARdQVDAACAPwmeBUMAAAAACZ4FQwAAgD8BxwVDAAAAAAHHBUMAAIA/+e8FQwAAAAD57wVDAACAP/AYBkMAAAAA8BgGQwAAgD/oQQZDAAAAAOhBBkMAAIA/4GoGQwAAAADgagZDAACAP9iTBkMAAAAA2JMGQwAAgD/QvAZDAAAAANC8BkMAAIA/x+UGQwAAAADH5QZDAACAP78OB0MAAAAAvw4HQwAAgD+2NwdDAAAAALY3B0MAAIA/rmAHQwAAAACuYAdDAACAP6aJB0MAAAAApokHQwAAgD+csgdDAAAAAJyyB0MAAIA/k9sHQwAAAACT2wdDAACAP4sECEMAAAAAiwQIQwAAgD+CLQhDAAAAAIItCEMAAIA/elYIQwAAAAB6VghDAACAP3B/CEMAAAAAcH8IQwAAgD9nqAhDAAAAAGeoCEMAAIA/X9EIQwAAAABf0QhDAACAP1X6CEMAAAAAVfoIQwAAgD9NIwlDAAAAAE0jCUMAAIA/Q0wJQwAAAABDTAlDAACAPzp1CUMAAAAAOnUJQwAAgD8ynglDAAAAADKeCUMAAIA/KMcJQwAAAAAoxwlDAACAPyDwCUMAAAAAIPAJQwAAgD8VGQpDAAAAABUZCkMAAIA/DUIKQwAAAAANQgpDAACAPwVrCkMAAAAABWsKQwAAgD/9kwpDAAAAAP2TCkMAAIA/9LwKQwAAAAD0vApDAACAP+rlCkMAAAAA6uUKQwAAgD/iDgtDAAAAAOIOC0MAAIA/2jcLQwAAAADaNwtDAACAP9FgC0MAAAAA0WALQwAAgD/JiQtDAAAAAMmJC0MAAIA/wLILQwAAAADAsgtDAACAP7fbC0MAAAAAt9sLQwAAgD+vBAxDAAAAAK8EDEMAAIA/pi0MQwAAAACmLQxDAACAP51WDEMAAAAAnVYMQwAAgD+VfwxDAAAAAJV/DEMAAIA/jKgMQwAAAACMqAxDAACAP4TRDEMAAAAAhNEMQwAAgD98+gxDAAAAAHz6DEMAAIA/ciMNQwAAAAByIw1DAACAP2pMDUMAAAAAakwNQwAAgD9idQ1DAAAAAGJ1DUMAAIA/WZ4NQwAAAABZng1DAACAP1HHDUMAAAAAUccNQwAAgD9J8A1DAAAAAEnwDUMAAIA/QBkOQwAAAABAGQ5DAACAPzhCDkMAAAAAOEIOQwAAgD8waw5DAAAAADBrDkMAAIA/KJQOQwAAAAAolA5DAACAPyC9DkMAAAAAIL0OQwAAgD8X5g5DAAAAABfmDkMAAIA/Dw8PQwAAAAAPDw9DAACAPwc4D0MAAAAABzgPQwAAgD//YA9DAAAAAP9gD0MAAIA/9okPQwAAAAD2iQ9DAACAP+6yD0MAAAAA7rIPQwAAgD/m2w9DAAAAAObbD0MAAIA/1wQQQwAAAADXBBBDAACAP8ktEEMAAAAAyS0QQwAAgD+7VhBDAAAAALtWEEMAAIA/rH8QQwAAAACsfxBDAACAP56oEEMAAAAAnqgQQwAAgD+S0RBDAAAAAJLREEMAAIA/ivoQQwAAAACK+hBDAACAP4EjEUMAAAAAgSMRQwAAgD95TBFDAAAAAHlMEUMAAIA/cXURQwAAAABxdRFDAACAP2meEUMAAAAAaZ4RQwAAgD9gxxFDAAAAAGDHEUMAAIA/WPARQwAAAABY8BFDAACAP1AZEkMAAAAAUBkSQwAAgD9IQhJDAAAAAEhCEkMAAIA/QGsSQwAAAABAaxJDAACAPzeUEkMAAAAAN5QSQwAAgD8vvRJDAAAAAC+9EkMAAIA/JOYSQwAAAAAk5hJDAACAPxkPE0MAAAAAGQ8TQwAAgD8OOBNDAAAAAA44E0MAAIA/A2ETQwAAAAADYRNDAACAP/iJE0MAAAAA+IkTQwAAgD/sshNDAAAAAOyyE0MAAIA/4dsTQwAAAADh2xNDAACAP9YEFEMAAAAA1gQUQwAAgD/LLRRDAAAAAMstFEMAAIA/wVYUQwAAAADBVhRDAACAP7h/FEMAAAAAuH8UQwAAgD+vqBRDAAAAAK+oFEMAAIA/ptEUQwAAAACm0RRDAACAP536FEMAAAAAnfoUQwAAgD+TIxVDAAAAAJMjFUMAAIA/iUwVQwAAAACJTBVDAACAP351FUMAAAAAfnUVQwAAgD9znhVDAAAAAHOeFUMAAIA/aMcVQwAAAABoxxVDAACAP13wFUMAAAAAXfAVQwAAgD9SGRZDAAAAAFIZFkMAAIA/R0IWQwAAAABHQhZDAACAPzxrFkMAAAAAPGsWQwAAgD8xlBZDAAAAADGUFkMAAIA/KL0WQwAAAAAovRZDAACAPyDmFkMAAAAAIOYWQwAAgD8YDxdDAAAAABgPF0MAAIA/DzgXQwAAAAAPOBdDAACAPwdhF0MAAAAAB2EXQwAAgD//iRdDAAAAAP+JF0MAAIA/97IXQwAAAAD3shdDAACAP+/bF0MAAAAA79sXQwAAgD/mBBhDAAAAAOYEGEMAAIA/3i0YQwAAAADeLRhDAACAP9ZWGEMAAAAA1lYYQwAAgD/OfxhDAAAAAM5/GEMAAIA/xqgYQwAAAADGqBhDAACAP73RGEMAAAAAvdEYQwAAgD+1+hhDAAAAALX6GEMAAIA/nSMZQwAAAACdIxlDAACAP4ZMGUMAAAAAhkwZQwAAgD9vdRlDAAAAAG91GUMAAIA/Zp4ZQwAAAABmnhlDAACAP17HGUMAAAAAXscZQwAAgD9W8BlDAAAAAFbwGUMAAIA/TRkaQwAAAABNGRpDAACAP0VCGkMAAAAARUIaQwAAgD89axpDAAAAAD1rGkMAAIA/NZQaQwAAAAA1lBpDAACAPy29GkMAAAAALb0aQwAAgD8k5hpDAAAAACTmGkMAAIA/HA8bQwAAAAAcDxtDAACAPxQ4G0MAAAAAFDgbQwAAgD8MYRtDAAAAAAxhG0MAAIA/BIobQwAAAAAEihtDAACAP/uyG0MAAAAA+7IbQwAAgD/z2xtDAAAAAPPbG0MAAIA/6wQcQwAAAADrBBxDAACAP+MtHEMAAAAA4y0cQwAAgD/bVhxDAAAAANtWHEMAAIA/0n8cQwAAAADSfxxDAACAP8qoHEMAAAAAyqgcQwAAgD+/0RxDAAAAAL/RHEMAAIA/tPocQwAAAAC0+hxDAACAP6kjHUMAAAAAqSMdQwAAgD+eTB1DAAAAAJ5MHUMAAIA/k3UdQwAAAACTdR1DAACAP4ieHUMAAAAAiJ4dQwAAgD98xx1DAAAAAHzHHUMAAIA/cfAdQwAAAABx8B1DAACAP2cZHkMAAAAAZxkeQwAAgD9aQh5DAAAAAFpCHkMAAIA/UWseQwAAAABRax5DAACAP0mUHkMAAAAASZQeQwAAgD9BvR5DAAAAAEG9HkMAAIA/OeYeQwAAAAA55h5DAACAPzEPH0MAAAAAMQ8fQwAAgD8oOB9DAAAAACg4H0MAAIA/IGEfQwAAAAAgYR9DAACAPxiKH0MAAAAAGIofQwAAgD8Qsx9DAAAAABCzH0MAAIA/CNwfQwAAAAAI3B9DAACAP/8EIEMAAAAA/wQgQwAAgD/3LSBDAAAAAPctIEMAAIA/71YgQwAAAADvViBDAACAP+d/IEMAAAAA538gQwAAgD/fqCBDAAAAAN+oIEMAAIA/1dEgQwAAAADV0SBDAACAP836IEMAAAAAzfogQwAAgD/FIyFDAAAAAMUjIUMAAIA/vUwhQwAAAAC9TCFDAACAP7R1IUMAAAAAtHUhQwAAgD+sniFDAAAAAKyeIUMAAIA/pMchQwAAAACkxyFDAACAP5rwIUMAAAAAmvAhQwAAgD+SGSJDAAAAAJIZIkMAAIA/ikIiQwAAAACKQiJDAACAP4JrIkMAAAAAgmsiQwAAgD95lCJDAAAAAHmUIkMAAIA/cb0iQwAAAABxvSJDAACAP2nmIkMAAAAAaeYiQwAAgD9gDyNDAAAAAGAPI0MAAIA/WDgjQwAAAABYOCNDAACAP09hI0MAAAAAT2EjQwAAgD9HiiNDAAAAAEeKI0MAAIA/PrMjQwAAAAA+syNDAACAPzbcI0MAAAAANtwjQwAAgD8uBSRDAAAAAC4FJEMAAIA/Ji4kQwAAAAAmLiRDAACAPx1XJEMAAAAAHVckQwAAgD8VgCRDAAAAABWAJEMAAIA/DakkQwAAAAANqSRDAACAPwTSJEMAAAAABNIkQwAAgD/8+iRDAAAAAPz6JEMAAIA/9CMlQwAAAAD0IyVDAACAP+xMJUMAAAAA7EwlQwAAgD/kdSVDAAAAAOR1JUMAAIA/254lQwAAAADbniVDAACAP9PHJUMAAAAA08clQwAAgD/L8CVDAAAAAMvwJUMAAIA/wxkmQwAAAADDGSZDAACAP7pCJkMAAAAAukImQwAAgD+yayZDAAAAALJrJkMAAIA/qpQmQwAAAACqlCZDAACAP6K9JkMAAAAAor0mQwAAgD+a5iZDAAAAAJrmJkMAAIA/kQ8nQwAAAACRDydDAACAP4k4J0MAAAAAiTgnQwAAgD+BYSdDAAAAAIFhJ0MAAIA/eYonQwAAAAB5iidDAACAP86yJ0MAAAAAzrInQwAAgD/F2ydDAAAAAMXbJ0MAAIA/vQQoQwAAAAC9BChDAACAP7UtKEMAAAAAtS0oQwAAgD+tVihDAAAAAK1WKEMAAIA/pH8oQwAAAACkfyhDAACAP5yoKEMAAAAAnKgoQwAAgD+U0ShDAAAAAJTRKEMAAIA/jPooQwAAAACM+ihDAACAP4QjKUMAAAAAhCMpQwAAgD97TClDAAAAAHtMKUMAAIA/c3UpQwAAAABzdSlDAACAP2ueKUMAAAAAa54pQwAAgD9jxylDAAAAAGPHKUMAAIA/V/ApQwAAAABX8ClDAACAP08ZKkMAAAAATxkqQwAAgD9HQipDAAAAAEdCKkMAAIA/PmsqQwAAAAA+aypDAACAPzCUKkMAAAAAMJQqQwAAgD8ovSpDAAAAACi9KkMAAIA/IOYqQwAAAAAg5ipDAACAPxcPK0MAAAAAFw8rQwAAgD8MOCtDAAAAAAw4K0MAAIA/BGErQwAAAAAEYStDAACAP/yJK0MAAAAA/IkrQwAAgD/zsitDAAAAAPOyK0MAAIA/69srQwAAAADr2ytDAACAP+EELEMAAAAA4QQsQwAAgD/YLSxDAAAAANgtLEMAAIA/0FYsQwAAAADQVixDAACAP8h/LEMAAAAAyH8sQwAAgD/AqCxDAAAAAMCoLEMAAIA/ttEsQwAAAAC20SxDAACAP636LEMAAAAArfosQwAAgD+lIy1DAAAAAKUjLUMAAIA/nUwtQwAAAACdTC1DAACAP5V1LUMAAAAAlXUtQwAAgD+Nni1DAAAAAI2eLUMAAIA/hMctQwAAAACExy1DAACAP3vwLUMAAAAAe/AtQwAAgD9zGS5DAAAAAHMZLkMAAIA/a0IuQwAAAABrQi5DAACAP2NrLkMAAAAAY2suQwAAgD9blC5DAAAAAFuULkMAAIA/Ur0uQwAAAABSvS5DAACAP0nmLkMAAAAASeYuQwAAgD9BDy9DAAAAAEEPL0MAAIA/OTgvQwAAAAA5OC9DAACAPzFhL0MAAAAAMWEvQwAAgD8pii9DAAAAACmKL0MAAIA/"), "format": 34359742487, -"index_count": 5436, -"index_data": PackedByteArray("AAACAAEAAQACAAMAAgAEAAMAAwAEAAUABAAGAAUABQAGAAcABgAIAAcABwAIAAkACAAKAAkACQAKAAsACgAMAAsACwAMAA0ADAAOAA0ADQAOAA8ADgAQAA8ADwAQABEAEAASABEAEQASABMAEgAUABMAEwAUABUAFAAWABUAFQAWABcAFgAYABcAFwAYABkAGAAaABkAGQAaABsAGgAcABsAGwAcAB0AHAAeAB0AHQAeAB8AHgAgAB8AHwAgACEAIAAiACEAIQAiACMAIgAkACMAIwAkACUAJAAmACUAJQAmACcAJgAoACcAJwAoACkAKAAqACkAKQAqACsAKgAsACsAKwAsAC0ALAAuAC0ALQAuAC8ALgAwAC8ALwAwADEAMAAyADEAMQAyADMAMgA0ADMAMwA0ADUANAA2ADUANQA2ADcANgA4ADcANwA4ADkAOAA6ADkAOQA6ADsAOgA8ADsAOwA8AD0APAA+AD0APQA+AD8APgBAAD8APwBAAEEAQABCAEEAQQBCAEMAQgBEAEMAQwBEAEUARABGAEUARQBGAEcARgBIAEcARwBIAEkASABKAEkASQBKAEsASgBMAEsASwBMAE0ATABOAE0ATQBOAE8ATgBQAE8ATwBQAFEAUABSAFEAUQBSAFMAUgBUAFMAUwBUAFUAVABWAFUAVQBWAFcAVgBYAFcAVwBYAFkAWABaAFkAWQBaAFsAWgBcAFsAWwBcAF0AXABeAF0AXQBeAF8AXgBgAF8AXwBgAGEAYABiAGEAYQBiAGMAYgBkAGMAYwBkAGUAZABmAGUAZQBmAGcAZgBoAGcAZwBoAGkAaABqAGkAaQBqAGsAagBsAGsAawBsAG0AbABuAG0AbQBuAG8AbgBwAG8AbwBwAHEAcAByAHEAcQByAHMAcgB0AHMAcwB0AHUAdAB2AHUAdQB2AHcAdgB4AHcAdwB4AHkAeAB6AHkAeQB6AHsAegB8AHsAewB8AH0AfAB+AH0AfQB+AH8AfgCAAH8AfwCAAIEAgACCAIEAgQCCAIMAggCEAIMAgwCEAIUAhACGAIUAhQCGAIcAhgCIAIcAhwCIAIkAiACKAIkAiQCKAIsAigCMAIsAiwCMAI0AjACOAI0AjQCOAI8AjgCQAI8AjwCQAJEAkACSAJEAkQCSAJMAkgCUAJMAkwCUAJUAlACWAJUAlQCWAJcAlgCYAJcAlwCYAJkAmACaAJkAmQCaAJsAmgCcAJsAmwCcAJ0AnACeAJ0AnQCeAJ8AngCgAJ8AnwCgAKEAoACiAKEAoQCiAKMAogCkAKMAowCkAKUApACmAKUApQCmAKcApgCoAKcApwCoAKkAqACqAKkAqQCqAKsAqgCsAKsAqwCsAK0ArACuAK0ArQCuAK8ArgCwAK8ArwCwALEAsACyALEAsQCyALMAsgC0ALMAswC0ALUAtAC2ALUAtQC2ALcAtgC4ALcAtwC4ALkAuAC6ALkAuQC6ALsAugC8ALsAuwC8AL0AvAC+AL0AvQC+AL8AvgDAAL8AvwDAAMEAwADCAMEAwQDCAMMAwgDEAMMAwwDEAMUAxADGAMUAxQDGAMcAxgDIAMcAxwDIAMkAyADKAMkAyQDKAMsAygDMAMsAywDMAM0AzADOAM0AzQDOAM8AzgDQAM8AzwDQANEA0ADSANEA0QDSANMA0gDUANMA0wDUANUA1ADWANUA1QDWANcA1gDYANcA1wDYANkA2ADaANkA2QDaANsA2gDcANsA2wDcAN0A3ADeAN0A3QDeAN8A3gDgAN8A3wDgAOEA4ADiAOEA4QDiAOMA4gDkAOMA4wDkAOUA5ADmAOUA5QDmAOcA5gDoAOcA5wDoAOkA6ADqAOkA6QDqAOsA6gDsAOsA6wDsAO0A7ADuAO0A7QDuAO8A7gDwAO8A7wDwAPEA8ADyAPEA8QDyAPMA8gD0APMA8wD0APUA9AD2APUA9QD2APcA9gD4APcA9wD4APkA+AD6APkA+QD6APsA+gD8APsA+wD8AP0A/AD+AP0A/QD+AP8A/gAAAf8A/wAAAQEBAAECAQEBAQECAQMBAgEEAQMBAwEEAQUBBAEGAQUBBQEGAQcBBgEIAQcBBwEIAQkBCAEKAQkBCQEKAQsBCgEMAQsBCwEMAQ0BDAEOAQ0BDQEOAQ8BDgEQAQ8BDwEQAREBEAESAREBEQESARMBEgEUARMBEwEUARUBFAEWARUBFQEWARcBFgEYARcBFwEYARkBGAEaARkBGQEaARsBGgEcARsBGwEcAR0BHAEeAR0BHQEeAR8BHgEgAR8BHwEgASEBIAEiASEBIQEiASMBIgEkASMBIwEkASUBJAEmASUBJQEmAScBJgEoAScBJwEoASkBKAEqASkBKQEqASsBKgEsASsBKwEsAS0BLAEuAS0BLQEuAS8BLgEwAS8BLwEwATEBMAEyATEBMQEyATMBMgE0ATMBMwE0ATUBNAE2ATUBNQE2ATcBNgE4ATcBNwE4ATkBOAE6ATkBOQE6ATsBOgE8ATsBOwE8AT0BPAE+AT0BPQE+AT8BPgFAAT8BPwFAAUEBQAFCAUEBQQFCAUMBQgFEAUMBQwFEAUUBRAFGAUUBRQFGAUcBRgFIAUcBRwFIAUkBSAFKAUkBSQFKAUsBSgFMAUsBSwFMAU0BTAFOAU0BTQFOAU8BTgFQAU8BTwFQAVEBUAFSAVEBUQFSAVMBUgFUAVMBUwFUAVUBVAFWAVUBVQFWAVcBVgFYAVcBVwFYAVkBWAFaAVkBWQFaAVsBWgFcAVsBWwFcAV0BXAFeAV0BXQFeAV8BXgFgAV8BXwFgAWEBYAFiAWEBYQFiAWMBYgFkAWMBYwFkAWUBZAFmAWUBZQFmAWcBZgFoAWcBZwFoAWkBaAFqAWkBaQFqAWsBagFsAWsBawFsAW0BbAFuAW0BbQFuAW8BbgFwAW8BbwFwAXEBcAFyAXEBcQFyAXMBcgF0AXMBcwF0AXUBdAF2AXUBdQF2AXcBdgF4AXcBdwF4AXkBeAF6AXkBeQF6AXsBegF8AXsBewF8AX0BfAF+AX0BfQF+AX8BfgGAAX8BfwGAAYEBgAGCAYEBgQGCAYMBggGEAYMBgwGEAYUBhAGGAYUBhQGGAYcBhgGIAYcBhwGIAYkBiAGKAYkBiQGKAYsBigGMAYsBiwGMAY0BjAGOAY0BjQGOAY8BjgGQAY8BjwGQAZEBkAGSAZEBkQGSAZMBkgGUAZMBkwGUAZUBlAGWAZUBlQGWAZcBlgGYAZcBlwGYAZkBmAGaAZkBmQGaAZsBmgGcAZsBmwGcAZ0BnAGeAZ0BnQGeAZ8BngGgAZ8BnwGgAaEBoAGiAaEBoQGiAaMBogGkAaMBowGkAaUBpAGmAaUBpQGmAacBpgGoAacBpwGoAakBqAGqAakBqQGqAasBqgGsAasBqwGsAa0BrAGuAa0BrQGuAa8BrgGwAa8BrwGwAbEBsAGyAbEBsQGyAbMBsgG0AbMBswG0AbUBtAG2AbUBtQG2AbcBtgG4AbcBtwG4AbkBuAG6AbkBuQG6AbsBugG8AbsBuwG8Ab0BvAG+Ab0BvQG+Ab8BvgHAAb8BvwHAAcEBwAHCAcEBwQHCAcMBwgHEAcMBwwHEAcUBxAHGAcUBxQHGAccBxgHIAccBxwHIAckByAHKAckByQHKAcsBygHMAcsBywHMAc0BzAHOAc0BzQHOAc8BzgHQAc8BzwHQAdEB0AHSAdEB0QHSAdMB0gHUAdMB0wHUAdUB1AHWAdUB1QHWAdcB1gHYAdcB1wHYAdkB2AHaAdkB2QHaAdsB2gHcAdsB2wHcAd0B3AHeAd0B3QHeAd8B3gHgAd8B3wHgAeEB4AHiAeEB4QHiAeMB4gHkAeMB4wHkAeUB5AHmAeUB5QHmAecB5gHoAecB5wHoAekB6AHqAekB6QHqAesB6gHsAesB6wHsAe0B7AHuAe0B7QHuAe8B7gHwAe8B7wHwAfEB8AHyAfEB8QHyAfMB8gH0AfMB8wH0AfUB9AH2AfUB9QH2AfcB9gH4AfcB9wH4AfkB+AH6AfkB+QH6AfsB+gH8AfsB+wH8Af0B/AH+Af0B/QH+Af8B/gEAAv8B/wEAAgECAAICAgECAQICAgMCAgIEAgMCAwIEAgUCBAIGAgUCBQIGAgcCBgIIAgcCBwIIAgkCCAIKAgkCCQIKAgsCCgIMAgsCCwIMAg0CDAIOAg0CDQIOAg8CDgIQAg8CDwIQAhECEAISAhECEQISAhMCEgIUAhMCEwIUAhUCFAIWAhUCFQIWAhcCFgIYAhcCFwIYAhkCGAIaAhkCGQIaAhsCGgIcAhsCGwIcAh0CHAIeAh0CHQIeAh8CHgIgAh8CHwIgAiECIAIiAiECIQIiAiMCIgIkAiMCIwIkAiUCJAImAiUCJQImAicCJgIoAicCJwIoAikCKAIqAikCKQIqAisCKgIsAisCKwIsAi0CLAIuAi0CLQIuAi8CLgIwAi8CLwIwAjECMAIyAjECMQIyAjMCMgI0AjMCMwI0AjUCNAI2AjUCNQI2AjcCNgI4AjcCNwI4AjkCOAI6AjkCOQI6AjsCOgI8AjsCOwI8Aj0CPAI+Aj0CPQI+Aj8CPgJAAj8CPwJAAkECQAJCAkECQQJCAkMCQgJEAkMCQwJEAkUCRAJGAkUCRQJGAkcCRgJIAkcCRwJIAkkCSAJKAkkCSQJKAksCSgJMAksCSwJMAk0CTAJOAk0CTQJOAk8CTgJQAk8CTwJQAlECUAJSAlECUQJSAlMCUgJUAlMCUwJUAlUCVAJWAlUCVQJWAlcCVgJYAlcCVwJYAlkCWAJaAlkCWQJaAlsCWgJcAlsCWwJcAl0CXAJeAl0CXQJeAl8CXgJgAl8CXwJgAmECYAJiAmECYQJiAmMCYgJkAmMCYwJkAmUCZAJmAmUCZQJmAmcCZgJoAmcCZwJoAmkCaAJqAmkCaQJqAmsCagJsAmsCawJsAm0CbAJuAm0CbQJuAm8CbgJwAm8CbwJwAnECcAJyAnECcQJyAnMCcgJ0AnMCcwJ0AnUCdAJ2AnUCdQJ2AncCdgJ4AncCdwJ4AnkCeAJ6AnkCeQJ6AnsCegJ8AnsCewJ8An0CfAJ+An0CfQJ+An8CfgKAAn8CfwKAAoECgAKCAoECgQKCAoMCggKEAoMCgwKEAoUChAKGAoUChQKGAocChgKIAocChwKIAokCiAKKAokCiQKKAosCigKMAosCiwKMAo0CjAKOAo0CjQKOAo8CjgKQAo8CjwKQApECkAKSApECkQKSApMCkgKUApMCkwKUApUClAKWApUClQKWApcClgKYApcClwKYApkCmAKaApkCmQKaApsCmgKcApsCmwKcAp0CnAKeAp0CnQKeAp8CngKgAp8CnwKgAqECoAKiAqECoQKiAqMCogKkAqMCowKkAqUCpAKmAqUCpQKmAqcCpgKoAqcCpwKoAqkCqAKqAqkCqQKqAqsCqgKsAqsCqwKsAq0CrAKuAq0CrQKuAq8CrgKwAq8CrwKwArECsAKyArECsQKyArMCsgK0ArMCswK0ArUCtAK2ArUCtQK2ArcCtgK4ArcCtwK4ArkCuAK6ArkCuQK6ArsCugK8ArsCuwK8Ar0CvAK+Ar0CvQK+Ar8CvgLAAr8CvwLAAsECwALCAsECwQLCAsMCwgLEAsMCwwLEAsUCxALGAsUCxQLGAscCxgLIAscCxwLIAskCyALKAskCyQLKAssCygLMAssCywLMAs0CzALOAs0CzQLOAs8CzgLQAs8CzwLQAtEC0ALSAtEC0QLSAtMC0gLUAtMC0wLUAtUC1ALWAtUC1QLWAtcC1gLYAtcC1wLYAtkC2ALaAtkC2QLaAtsC2gLcAtsC2wLcAt0C3ALeAt0C3QLeAt8C3gLgAt8C3wLgAuEC4ALiAuEC4QLiAuMC4gLkAuMC4wLkAuUC5ALmAuUC5QLmAucC5gLoAucC5wLoAukC6ALqAukC6QLqAusC6gLsAusC6wLsAu0C7ALuAu0C7QLuAu8C7gLwAu8C7wLwAvEC8ALyAvEC8QLyAvMC8gL0AvMC8wL0AvUC9AL2AvUC9QL2AvcC9gL4AvcC9wL4AvkC+AL6AvkC+QL6AvsC+gL8AvsC+wL8Av0C/AL+Av0C/QL+Av8C/gIAA/8C/wIAAwEDAAMCAwEDAQMCAwMDAgMEAwMDAwMEAwUDBAMGAwUDBQMGAwcDBgMIAwcDBwMIAwkDCAMKAwkDCQMKAwsDCgMMAwsDCwMMAw0DDAMOAw0DDQMOAw8DDgMQAw8DDwMQAxEDEAMSAxEDEQMSAxMDEgMUAxMDEwMUAxUDFAMWAxUDFQMWAxcDFgMYAxcDFwMYAxkDGAMaAxkDGQMaAxsDGgMcAxsDGwMcAx0DHAMeAx0DHQMeAx8DHgMgAx8DHwMgAyEDIAMiAyEDIQMiAyMDIgMkAyMDIwMkAyUDJAMmAyUDJQMmAycDJgMoAycDJwMoAykDKAMqAykDKQMqAysDKgMsAysDKwMsAy0DLAMuAy0DLQMuAy8DLgMwAy8DLwMwAzEDMAMyAzEDMQMyAzMDMgM0AzMDMwM0AzUDNAM2AzUDNQM2AzcDNgM4AzcDNwM4AzkDOAM6AzkDOQM6AzsDOgM8AzsDOwM8Az0DPAM+Az0DPQM+Az8DPgNAAz8DPwNAA0EDQANCA0EDQQNCA0MDQgNEA0MDQwNEA0UDRANGA0UDRQNGA0cDRgNIA0cDRwNIA0kDSANKA0kDSQNKA0sDSgNMA0sDSwNMA00DTANOA00DTQNOA08DTgNQA08DTwNQA1EDUANSA1EDUQNSA1MDUgNUA1MDUwNUA1UDVANWA1UDVQNWA1cDVgNYA1cDVwNYA1kDWANaA1kDWQNaA1sDWgNcA1sDWwNcA10DXANeA10DXQNeA18DXgNgA18DXwNgA2EDYANiA2EDYQNiA2MDYgNkA2MDYwNkA2UDZANmA2UDZQNmA2cDZgNoA2cDZwNoA2kDaANqA2kDaQNqA2sDagNsA2sDawNsA20DbANuA20DbQNuA28DbgNwA28DbwNwA3EDcANyA3EDcQNyA3MDcgN0A3MDcwN0A3UDdAN2A3UDdQN2A3cDdgN4A3cDdwN4A3kDeAN6A3kDeQN6A3sDegN8A3sDewN8A30DfAN+A30DfQN+A38DfgOAA38DfwOAA4EDgAOCA4EDgQOCA4MDggOEA4MDgwOEA4UDhAOGA4UDhQOGA4cDhgOIA4cDhwOIA4kDiAOKA4kDiQOKA4sDigOMA4sDiwOMA40DjAOOA40DjQOOA48DjgOQA48DjwOQA5EDkAOSA5EDkQOSA5MDkgOUA5MDkwOUA5UDlAOWA5UDlQOWA5cDlgOYA5cDlwOYA5kDmAOaA5kDmQOaA5sDmgOcA5sDmwOcA50DnAOeA50DnQOeA58DngOgA58DnwOgA6EDoAOiA6EDoQOiA6MDogOkA6MDowOkA6UDpAOmA6UDpQOmA6cDpgOoA6cDpwOoA6kDqAOqA6kDqQOqA6sDqgOsA6sDqwOsA60DrAOuA60DrQOuA68DrgOwA68DrwOwA7EDsAOyA7EDsQOyA7MDsgO0A7MDswO0A7UDtAO2A7UDtQO2A7cDtgO4A7cDtwO4A7kDuAO6A7kDuQO6A7sDugO8A7sDuwO8A70DvAO+A70DvQO+A78DvgPAA78DvwPAA8EDwAPCA8EDwQPCA8MDwgPEA8MDwwPEA8UDxAPGA8UDxQPGA8cDxgPIA8cDxwPIA8kDyAPKA8kDyQPKA8sDygPMA8sDywPMA80DzAPOA80DzQPOA88DzgPQA88DzwPQA9ED0APSA9ED0QPSA9MD0gPUA9MD0wPUA9UD1APWA9UD1QPWA9cD1gPYA9cD1wPYA9kD2APaA9kD2QPaA9sD2gPcA9sD2wPcA90D3APeA90D3QPeA98D3gPgA98D3wPgA+ED4APiA+ED4QPiA+MD4gPkA+MD4wPkA+UD5APmA+UD5QPmA+cD5gPoA+cD5wPoA+kD6APqA+kD6QPqA+sD6gPsA+sD6wPsA+0D7APuA+0D7QPuA+8D7gPwA+8D7wPwA/ED8APyA/ED8QPyA/MD8gP0A/MD8wP0A/UD9AP2A/UD9QP2A/cD9gP4A/cD9wP4A/kD+AP6A/kD+QP6A/sD+gP8A/sD+wP8A/0D/AP+A/0D/QP+A/8D/gMABP8D/wMABAEEAAQCBAEEAQQCBAMEAgQEBAMEAwQEBAUEBAQGBAUEBQQGBAcEBgQIBAcEBwQIBAkECAQKBAkECQQKBAsECgQMBAsECwQMBA0EDAQOBA0EDQQOBA8EDgQQBA8EDwQQBBEEEAQSBBEEEQQSBBMEEgQUBBMEEwQUBBUEFAQWBBUEFQQWBBcEFgQYBBcEFwQYBBkEGAQaBBkEGQQaBBsEGgQcBBsEGwQcBB0EHAQeBB0EHQQeBB8EHgQgBB8EHwQgBCEEIAQiBCEEIQQiBCMEIgQkBCMEIwQkBCUEJAQmBCUEJQQmBCcEJgQoBCcEJwQoBCkEKAQqBCkEKQQqBCsEKgQsBCsEKwQsBC0ELAQuBC0ELQQuBC8ELgQwBC8ELwQwBDEEMAQyBDEEMQQyBDMEMgQ0BDMEMwQ0BDUENAQ2BDUENQQ2BDcENgQ4BDcENwQ4BDkEOAQ6BDkEOQQ6BDsEOgQ8BDsEOwQ8BD0EPAQ+BD0EPQQ+BD8EPgRABD8EPwRABEEEQARCBEEEQQRCBEMEQgREBEMEQwREBEUERARGBEUERQRGBEcERgRIBEcERwRIBEkESARKBEkESQRKBEsESgRMBEsESwRMBE0ETAROBE0ETQROBE8ETgRQBE8ETwRQBFEEUARSBFEEUQRSBFMEUgRUBFMEUwRUBFUEVARWBFUEVQRWBFcEVgRYBFcEVwRYBFkEWARaBFkEWQRaBFsEWgRcBFsEWwRcBF0EXAReBF0EXQReBF8EXgRgBF8EXwRgBGEEYARiBGEEYQRiBGMEYgRkBGMEYwRkBGUEZARmBGUEZQRmBGcEZgRoBGcEZwRoBGkEaARqBGkEaQRqBGsEagRsBGsEawRsBG0EbARuBG0EbQRuBG8EbgRwBG8EbwRwBHEEcARyBHEEcQRyBHMEcgR0BHMEcwR0BHUEdAR2BHUEdQR2BHcEdgR4BHcEdwR4BHkEeAR6BHkEeQR6BHsEegR8BHsEewR8BH0EfAR+BH0EfQR+BH8EfgSABH8EfwSABIEEgASCBIEEgQSCBIMEggSEBIMEgwSEBIUEhASGBIUEhQSGBIcEhgSIBIcEhwSIBIkEiASKBIkEiQSKBIsEigSMBIsEiwSMBI0EjASOBI0EjQSOBI8EjgSQBI8EjwSQBJEEkASSBJEEkQSSBJMEkgSUBJMEkwSUBJUElASWBJUElQSWBJcElgSYBJcElwSYBJkEmASaBJkEmQSaBJsEmgScBJsEmwScBJ0EnASeBJ0EnQSeBJ8EngSgBJ8EnwSgBKEEoASiBKEEoQSiBKMEogSkBKMEowSkBKUEpASmBKUEpQSmBKcEpgSoBKcEpwSoBKkEqASqBKkEqQSqBKsEqgSsBKsEqwSsBK0ErASuBK0ErQSuBK8ErgSwBK8ErwSwBLEEsASyBLEEsQSyBLMEsgS0BLMEswS0BLUEtAS2BLUEtQS2BLcEtgS4BLcEtwS4BLkEuAS6BLkEuQS6BLsEugS8BLsEuwS8BL0EvAS+BL0EvQS+BL8EvgTABL8EvwTABMEEwATCBMEEwQTCBMMEwgTEBMMEwwTEBMUExATGBMUExQTGBMcExgTIBMcExwTIBMkEyATKBMkEyQTKBMsEygTMBMsEywTMBM0EzATOBM0EzQTOBM8EzgTQBM8EzwTQBNEE0ATSBNEE0QTSBNME0gTUBNME0wTUBNUE1ATWBNUE1QTWBNcE1gTYBNcE1wTYBNkE2ATaBNkE2QTaBNsE2gTcBNsE2wTcBN0E3ATeBN0E3QTeBN8E3gTgBN8E3wTgBOEE4ATiBOEE4QTiBOME4gTkBOME4wTkBOUE5ATmBOUE5QTmBOcE5gToBOcE5wToBOkE6ATqBOkE6QTqBOsE6gTsBOsE6wTsBO0E7ATuBO0E7QTuBO8E7gTwBO8E7wTwBPEE8ATyBPEE8QTyBPME8gT0BPME8wT0BPUE9AT2BPUE9QT2BPcE9gT4BPcE9wT4BPkE+AT6BPkE+QT6BPsE+gT8BPsE+wT8BP0E/AT+BP0E/QT+BP8E/gQABf8E/wQABQEFAAUCBQEFAQUCBQMFAgUEBQMFAwUEBQUFBAUGBQUFBQUGBQcFBgUIBQcFBwUIBQkFCAUKBQkFCQUKBQsFCgUMBQsFCwUMBQ0FDAUOBQ0FDQUOBQ8FDgUQBQ8FDwUQBREFEAUSBREFEQUSBRMFEgUUBRMFEwUUBRUFFAUWBRUFFQUWBRcFFgUYBRcFFwUYBRkFGAUaBRkFGQUaBRsFGgUcBRsFGwUcBR0FHAUeBR0FHQUeBR8FHgUgBR8FHwUgBSEFIAUiBSEFIQUiBSMFIgUkBSMFIwUkBSUFJAUmBSUFJQUmBScFJgUoBScFJwUoBSkFKAUqBSkFKQUqBSsFKgUsBSsFKwUsBS0FLAUuBS0FLQUuBS8FLgUwBS8FLwUwBTEFMAUyBTEFMQUyBTMFMgU0BTMFMwU0BTUFNAU2BTUFNQU2BTcFNgU4BTcFNwU4BTkFOAU6BTkFOQU6BTsFOgU8BTsFOwU8BT0FPAU+BT0FPQU+BT8FPgVABT8FPwVABUEFQAVCBUEFQQVCBUMFQgVEBUMFQwVEBUUFRAVGBUUFRQVGBUcFRgVIBUcFRwVIBUkFSAVKBUkFSQVKBUsFSgVMBUsFSwVMBU0FTAVOBU0FTQVOBU8FTgVQBU8FTwVQBVEFUAVSBVEFUQVSBVMFUgVUBVMFUwVUBVUFVAVWBVUFVQVWBVcFVgVYBVcFVwVYBVkFWAVaBVkFWQVaBVsFWgVcBVsFWwVcBV0FXAVeBV0FXQVeBV8FXgVgBV8FXwVgBWEFYAViBWEFYQViBWMFYgVkBWMFYwVkBWUFZAVmBWUFZQVmBWcFZgVoBWcFZwVoBWkFaAVqBWkFaQVqBWsFagVsBWsFawVsBW0FbAVuBW0FbQVuBW8FbgVwBW8FbwVwBXEFcAVyBXEFcQVyBXMFcgV0BXMFcwV0BXUFdAV2BXUFdQV2BXcFdgV4BXcFdwV4BXkFeAV6BXkFeQV6BXsFegV8BXsFewV8BX0FfAV+BX0FfQV+BX8FfgWABX8FfwWABYEFgAWCBYEFgQWCBYMFggWEBYMFgwWEBYUFhAWGBYUFhQWGBYcFhgWIBYcFhwWIBYkFiAWKBYkFiQWKBYsFigWMBYsFiwWMBY0FjAWOBY0FjQWOBY8FjgWQBY8FjwWQBZEFkAWSBZEFkQWSBZMFkgWUBZMFkwWUBZUFlAWWBZUFlQWWBZcFlgWYBZcFlwWYBZkFmAWaBZkFmQWaBZsFmgWcBZsFmwWcBZ0FnAWeBZ0FnQWeBZ8FngWgBZ8FnwWgBaEFoAWiBaEFoQWiBaMFogWkBaMFowWkBaUFpAWmBaUFpQWmBacFpgWoBacFpwWoBakFqAWqBakFqQWqBasFqgWsBasFqwWsBa0FrAWuBa0FrQWuBa8FrgWwBa8FrwWwBbEFsAWyBbEFsQWyBbMFsgW0BbMFswW0BbUFtAW2BbUFtQW2BbcFtgW4BbcFtwW4BbkFuAW6BbkFuQW6BbsFugW8BbsFuwW8Bb0FvAW+Bb0FvQW+Bb8FvgXABb8FvwXABcEFwAXCBcEFwQXCBcMFwgXEBcMFwwXEBcUFxAXGBcUFxQXGBccFxgXIBccFxwXIBckFyAXKBckFyQXKBcsFygXMBcsFywXMBc0FzAXOBc0FzQXOBc8FzgXQBc8FzwXQBdEF0AXSBdEF0QXSBdMF0gXUBdMF0wXUBdUF1AXWBdUF1QXWBdcF1gXYBdcF1wXYBdkF2AXaBdkF2QXaBdsF2gXcBdsF2wXcBd0F3AXeBd0F3QXeBd8F3gXgBd8F3wXgBeEF4AXiBeEF4QXiBeMF4gXkBeMF4wXkBeUF5AXmBeUF5QXmBecF5gXoBecF5wXoBekF6AXqBekF6QXqBesF6gXsBesF6wXsBe0F7AXuBe0F7QXuBe8F7gXwBe8F7wXwBfEF8AXyBfEF8QXyBfMF8gX0BfMF8wX0BfUF9AX2BfUF9QX2BfcF9gX4BfcF9wX4BfkF+AX6BfkF+QX6BfsF+gX8BfsF+wX8Bf0F/AX+Bf0F/QX+Bf8F/gUABv8F/wUABgEGAAYCBgEGAQYCBgMGAgYEBgMGAwYEBgUGBAYGBgUGBQYGBgcGBgYIBgcGBwYIBgkGCAYKBgkGCQYKBgsGCgYMBgsGCwYMBg0GDAYOBg0GDQYOBg8GDgYQBg8GDwYQBhEGEAYSBhEGEQYSBhMGEgYUBhMGEwYUBhUGFAYWBhUGFQYWBhcGFgYYBhcGFwYYBhkGGAYaBhkGGQYaBhsGGgYcBhsGGwYcBh0GHAYeBh0GHQYeBh8GHgYgBh8GHwYgBiEGIAYiBiEGIQYiBiMGIgYkBiMGIwYkBiUGJAYmBiUGJQYmBicGJgYoBicGJwYoBikGKAYqBikGKQYqBisGKgYsBisGKwYsBi0GLAYuBi0GLQYuBi8GLgYwBi8GLwYwBjEGMAYyBjEGMQYyBjMGMgY0BjMGMwY0BjUGNAY2BjUGNQY2BjcGNgY4BjcGNwY4BjkGOAY6BjkGOQY6BjsGOgY8BjsGOwY8Bj0GPAY+Bj0GPQY+Bj8GPgZABj8GPwZABkEGQAZCBkEGQQZCBkMGQgZEBkMGQwZEBkUGRAZGBkUGRQZGBkcGRgZIBkcGRwZIBkkGSAZKBkkGSQZKBksGSgZMBksGSwZMBk0GTAZOBk0GTQZOBk8GTgZQBk8GTwZQBlEGUAZSBlEGUQZSBlMGUgZUBlMGUwZUBlUGVAZWBlUGVQZWBlcGVgZYBlcGVwZYBlkGWAZaBlkGWQZaBlsGWgZcBlsGWwZcBl0GXAZeBl0GXQZeBl8GXgZgBl8GXwZgBmEGYAZiBmEGYQZiBmMGYgZkBmMGYwZkBmUGZAZmBmUGZQZmBmcGZgZoBmcGZwZoBmkGaAZqBmkGaQZqBmsGagZsBmsGawZsBm0GbAZuBm0GbQZuBm8GbgZwBm8GbwZwBnEGcAZyBnEGcQZyBnMGcgZ0BnMGcwZ0BnUGdAZ2BnUGdQZ2BncGdgZ4BncGdwZ4BnkGeAZ6BnkGeQZ6BnsGegZ8BnsGewZ8Bn0GfAZ+Bn0GfQZ+Bn8GfgaABn8GfwaABoEGgAaCBoEGgQaCBoMGggaEBoMGgwaEBoUGhAaGBoUGhQaGBocGhgaIBocGhwaIBokGiAaKBokGiQaKBosGigaMBosGiwaMBo0GjAaOBo0GjQaOBo8GjgaQBo8GjwaQBpEGkAaSBpEGkQaSBpMGkgaUBpMGkwaUBpUGlAaWBpUGlQaWBpcGlgaYBpcGlwaYBpkGmAaaBpkGmQaaBpsGmgacBpsGmwacBp0GnAaeBp0GnQaeBp8GngagBp8GnwagBqEGoAaiBqEGoQaiBqMGogakBqMGowakBqUGpAamBqUGpQamBqcGpgaoBqcGpwaoBqkGqAaqBqkGqQaqBqsGqgasBqsGqwasBq0GrAauBq0GrQauBq8GrgawBq8GrwawBrEGsAayBrEGsQayBrMGsga0BrMGswa0BrUGtAa2BrUGtQa2BrcGtga4BrcGtwa4BrkGuAa6BrkGuQa6BrsGuga8BrsGuwa8Br0GvAa+Br0GvQa+Br8GvgbABr8GvwbABsEGwAbCBsEGwQbCBsMGwgbEBsMGwwbEBsUGxAbGBsUGxQbGBscGxgbIBscGxwbIBskGyAbKBskGyQbKBssGygbMBssGywbMBs0GzAbOBs0GzQbOBs8GzgbQBs8GzwbQBtEG0AbSBtEG0QbSBtMG0gbUBtMG0wbUBtUG1AbWBtUG1QbWBtcG1gbYBtcG1wbYBtkG2AbaBtkG2QbaBtsG2gbcBtsG2wbcBt0G3AbeBt0G3QbeBt8G3gbgBt8G3wbgBuEG4AbiBuEG4QbiBuMG4gbkBuMG4wbkBuUG5AbmBuUG5QbmBucG5gboBucG5wboBukG6AbqBukG6QbqBusG6gbsBusG6wbsBu0G7AbuBu0G7QbuBu8G7gbwBu8G7wbwBvEG8AbyBvEG8QbyBvMG8gb0BvMG8wb0BvUG9Ab2BvUG9Qb2BvcG9gb4BvcG9wb4BvkG+Ab6BvkG+Qb6BvsG+gb8BvsG+wb8Bv0G/Ab+Bv0G/Qb+Bv8G/gYAB/8G/wYABwEHAAcCBwEHAQcCBwMHAgcEBwMHAwcEBwUHBAcGBwUHBQcGBwcHBgcIBwcHBwcIBwkHCAcKBwkHCQcKBwsHCgcMBwsHCwcMBw0HDAcOBw0HDQcOBw8HDgcQBw8HDwcQBxEHEAcSBxEHEQcSBxMHEgcAABMHEwcAAAEA"), +"index_count": 6588, +"index_data": PackedByteArray("AAACAAEAAQACAAMAAgAEAAMAAwAEAAUABAAGAAUABQAGAAcABgAIAAcABwAIAAkACAAKAAkACQAKAAsACgAMAAsACwAMAA0ADAAOAA0ADQAOAA8ADgAQAA8ADwAQABEAEAASABEAEQASABMAEgAUABMAEwAUABUAFAAWABUAFQAWABcAFgAYABcAFwAYABkAGAAaABkAGQAaABsAGgAcABsAGwAcAB0AHAAeAB0AHQAeAB8AHgAgAB8AHwAgACEAIAAiACEAIQAiACMAIgAkACMAIwAkACUAJAAmACUAJQAmACcAJgAoACcAJwAoACkAKAAqACkAKQAqACsAKgAsACsAKwAsAC0ALAAuAC0ALQAuAC8ALgAwAC8ALwAwADEAMAAyADEAMQAyADMAMgA0ADMAMwA0ADUANAA2ADUANQA2ADcANgA4ADcANwA4ADkAOAA6ADkAOQA6ADsAOgA8ADsAOwA8AD0APAA+AD0APQA+AD8APgBAAD8APwBAAEEAQABCAEEAQQBCAEMAQgBEAEMAQwBEAEUARABGAEUARQBGAEcARgBIAEcARwBIAEkASABKAEkASQBKAEsASgBMAEsASwBMAE0ATABOAE0ATQBOAE8ATgBQAE8ATwBQAFEAUABSAFEAUQBSAFMAUgBUAFMAUwBUAFUAVABWAFUAVQBWAFcAVgBYAFcAVwBYAFkAWABaAFkAWQBaAFsAWgBcAFsAWwBcAF0AXABeAF0AXQBeAF8AXgBgAF8AXwBgAGEAYABiAGEAYQBiAGMAYgBkAGMAYwBkAGUAZABmAGUAZQBmAGcAZgBoAGcAZwBoAGkAaABqAGkAaQBqAGsAagBsAGsAawBsAG0AbABuAG0AbQBuAG8AbgBwAG8AbwBwAHEAcAByAHEAcQByAHMAcgB0AHMAcwB0AHUAdAB2AHUAdQB2AHcAdgB4AHcAdwB4AHkAeAB6AHkAeQB6AHsAegB8AHsAewB8AH0AfAB+AH0AfQB+AH8AfgCAAH8AfwCAAIEAgACCAIEAgQCCAIMAggCEAIMAgwCEAIUAhACGAIUAhQCGAIcAhgCIAIcAhwCIAIkAiACKAIkAiQCKAIsAigCMAIsAiwCMAI0AjACOAI0AjQCOAI8AjgCQAI8AjwCQAJEAkACSAJEAkQCSAJMAkgCUAJMAkwCUAJUAlACWAJUAlQCWAJcAlgCYAJcAlwCYAJkAmACaAJkAmQCaAJsAmgCcAJsAmwCcAJ0AnACeAJ0AnQCeAJ8AngCgAJ8AnwCgAKEAoACiAKEAoQCiAKMAogCkAKMAowCkAKUApACmAKUApQCmAKcApgCoAKcApwCoAKkAqACqAKkAqQCqAKsAqgCsAKsAqwCsAK0ArACuAK0ArQCuAK8ArgCwAK8ArwCwALEAsACyALEAsQCyALMAsgC0ALMAswC0ALUAtAC2ALUAtQC2ALcAtgC4ALcAtwC4ALkAuAC6ALkAuQC6ALsAugC8ALsAuwC8AL0AvAC+AL0AvQC+AL8AvgDAAL8AvwDAAMEAwADCAMEAwQDCAMMAwgDEAMMAwwDEAMUAxADGAMUAxQDGAMcAxgDIAMcAxwDIAMkAyADKAMkAyQDKAMsAygDMAMsAywDMAM0AzADOAM0AzQDOAM8AzgDQAM8AzwDQANEA0ADSANEA0QDSANMA0gDUANMA0wDUANUA1ADWANUA1QDWANcA1gDYANcA1wDYANkA2ADaANkA2QDaANsA2gDcANsA2wDcAN0A3ADeAN0A3QDeAN8A3gDgAN8A3wDgAOEA4ADiAOEA4QDiAOMA4gDkAOMA4wDkAOUA5ADmAOUA5QDmAOcA5gDoAOcA5wDoAOkA6ADqAOkA6QDqAOsA6gDsAOsA6wDsAO0A7ADuAO0A7QDuAO8A7gDwAO8A7wDwAPEA8ADyAPEA8QDyAPMA8gD0APMA8wD0APUA9AD2APUA9QD2APcA9gD4APcA9wD4APkA+AD6APkA+QD6APsA+gD8APsA+wD8AP0A/AD+AP0A/QD+AP8A/gAAAf8A/wAAAQEBAAECAQEBAQECAQMBAgEEAQMBAwEEAQUBBAEGAQUBBQEGAQcBBgEIAQcBBwEIAQkBCAEKAQkBCQEKAQsBCgEMAQsBCwEMAQ0BDAEOAQ0BDQEOAQ8BDgEQAQ8BDwEQAREBEAESAREBEQESARMBEgEUARMBEwEUARUBFAEWARUBFQEWARcBFgEYARcBFwEYARkBGAEaARkBGQEaARsBGgEcARsBGwEcAR0BHAEeAR0BHQEeAR8BHgEgAR8BHwEgASEBIAEiASEBIQEiASMBIgEkASMBIwEkASUBJAEmASUBJQEmAScBJgEoAScBJwEoASkBKAEqASkBKQEqASsBKgEsASsBKwEsAS0BLAEuAS0BLQEuAS8BLgEwAS8BLwEwATEBMAEyATEBMQEyATMBMgE0ATMBMwE0ATUBNAE2ATUBNQE2ATcBNgE4ATcBNwE4ATkBOAE6ATkBOQE6ATsBOgE8ATsBOwE8AT0BPAE+AT0BPQE+AT8BPgFAAT8BPwFAAUEBQAFCAUEBQQFCAUMBQgFEAUMBQwFEAUUBRAFGAUUBRQFGAUcBRgFIAUcBRwFIAUkBSAFKAUkBSQFKAUsBSgFMAUsBSwFMAU0BTAFOAU0BTQFOAU8BTgFQAU8BTwFQAVEBUAFSAVEBUQFSAVMBUgFUAVMBUwFUAVUBVAFWAVUBVQFWAVcBVgFYAVcBVwFYAVkBWAFaAVkBWQFaAVsBWgFcAVsBWwFcAV0BXAFeAV0BXQFeAV8BXgFgAV8BXwFgAWEBYAFiAWEBYQFiAWMBYgFkAWMBYwFkAWUBZAFmAWUBZQFmAWcBZgFoAWcBZwFoAWkBaAFqAWkBaQFqAWsBagFsAWsBawFsAW0BbAFuAW0BbQFuAW8BbgFwAW8BbwFwAXEBcAFyAXEBcQFyAXMBcgF0AXMBcwF0AXUBdAF2AXUBdQF2AXcBdgF4AXcBdwF4AXkBeAF6AXkBeQF6AXsBegF8AXsBewF8AX0BfAF+AX0BfQF+AX8BfgGAAX8BfwGAAYEBgAGCAYEBgQGCAYMBggGEAYMBgwGEAYUBhAGGAYUBhQGGAYcBhgGIAYcBhwGIAYkBiAGKAYkBiQGKAYsBigGMAYsBiwGMAY0BjAGOAY0BjQGOAY8BjgGQAY8BjwGQAZEBkAGSAZEBkQGSAZMBkgGUAZMBkwGUAZUBlAGWAZUBlQGWAZcBlgGYAZcBlwGYAZkBmAGaAZkBmQGaAZsBmgGcAZsBmwGcAZ0BnAGeAZ0BnQGeAZ8BngGgAZ8BnwGgAaEBoAGiAaEBoQGiAaMBogGkAaMBowGkAaUBpAGmAaUBpQGmAacBpgGoAacBpwGoAakBqAGqAakBqQGqAasBqgGsAasBqwGsAa0BrAGuAa0BrQGuAa8BrgGwAa8BrwGwAbEBsAGyAbEBsQGyAbMBsgG0AbMBswG0AbUBtAG2AbUBtQG2AbcBtgG4AbcBtwG4AbkBuAG6AbkBuQG6AbsBugG8AbsBuwG8Ab0BvAG+Ab0BvQG+Ab8BvgHAAb8BvwHAAcEBwAHCAcEBwQHCAcMBwgHEAcMBwwHEAcUBxAHGAcUBxQHGAccBxgHIAccBxwHIAckByAHKAckByQHKAcsBygHMAcsBywHMAc0BzAHOAc0BzQHOAc8BzgHQAc8BzwHQAdEB0AHSAdEB0QHSAdMB0gHUAdMB0wHUAdUB1AHWAdUB1QHWAdcB1gHYAdcB1wHYAdkB2AHaAdkB2QHaAdsB2gHcAdsB2wHcAd0B3AHeAd0B3QHeAd8B3gHgAd8B3wHgAeEB4AHiAeEB4QHiAeMB4gHkAeMB4wHkAeUB5AHmAeUB5QHmAecB5gHoAecB5wHoAekB6AHqAekB6QHqAesB6gHsAesB6wHsAe0B7AHuAe0B7QHuAe8B7gHwAe8B7wHwAfEB8AHyAfEB8QHyAfMB8gH0AfMB8wH0AfUB9AH2AfUB9QH2AfcB9gH4AfcB9wH4AfkB+AH6AfkB+QH6AfsB+gH8AfsB+wH8Af0B/AH+Af0B/QH+Af8B/gEAAv8B/wEAAgECAAICAgECAQICAgMCAgIEAgMCAwIEAgUCBAIGAgUCBQIGAgcCBgIIAgcCBwIIAgkCCAIKAgkCCQIKAgsCCgIMAgsCCwIMAg0CDAIOAg0CDQIOAg8CDgIQAg8CDwIQAhECEAISAhECEQISAhMCEgIUAhMCEwIUAhUCFAIWAhUCFQIWAhcCFgIYAhcCFwIYAhkCGAIaAhkCGQIaAhsCGgIcAhsCGwIcAh0CHAIeAh0CHQIeAh8CHgIgAh8CHwIgAiECIAIiAiECIQIiAiMCIgIkAiMCIwIkAiUCJAImAiUCJQImAicCJgIoAicCJwIoAikCKAIqAikCKQIqAisCKgIsAisCKwIsAi0CLAIuAi0CLQIuAi8CLgIwAi8CLwIwAjECMAIyAjECMQIyAjMCMgI0AjMCMwI0AjUCNAI2AjUCNQI2AjcCNgI4AjcCNwI4AjkCOAI6AjkCOQI6AjsCOgI8AjsCOwI8Aj0CPAI+Aj0CPQI+Aj8CPgJAAj8CPwJAAkECQAJCAkECQQJCAkMCQgJEAkMCQwJEAkUCRAJGAkUCRQJGAkcCRgJIAkcCRwJIAkkCSAJKAkkCSQJKAksCSgJMAksCSwJMAk0CTAJOAk0CTQJOAk8CTgJQAk8CTwJQAlECUAJSAlECUQJSAlMCUgJUAlMCUwJUAlUCVAJWAlUCVQJWAlcCVgJYAlcCVwJYAlkCWAJaAlkCWQJaAlsCWgJcAlsCWwJcAl0CXAJeAl0CXQJeAl8CXgJgAl8CXwJgAmECYAJiAmECYQJiAmMCYgJkAmMCYwJkAmUCZAJmAmUCZQJmAmcCZgJoAmcCZwJoAmkCaAJqAmkCaQJqAmsCagJsAmsCawJsAm0CbAJuAm0CbQJuAm8CbgJwAm8CbwJwAnECcAJyAnECcQJyAnMCcgJ0AnMCcwJ0AnUCdAJ2AnUCdQJ2AncCdgJ4AncCdwJ4AnkCeAJ6AnkCeQJ6AnsCegJ8AnsCewJ8An0CfAJ+An0CfQJ+An8CfgKAAn8CfwKAAoECgAKCAoECgQKCAoMCggKEAoMCgwKEAoUChAKGAoUChQKGAocChgKIAocChwKIAokCiAKKAokCiQKKAosCigKMAosCiwKMAo0CjAKOAo0CjQKOAo8CjgKQAo8CjwKQApECkAKSApECkQKSApMCkgKUApMCkwKUApUClAKWApUClQKWApcClgKYApcClwKYApkCmAKaApkCmQKaApsCmgKcApsCmwKcAp0CnAKeAp0CnQKeAp8CngKgAp8CnwKgAqECoAKiAqECoQKiAqMCogKkAqMCowKkAqUCpAKmAqUCpQKmAqcCpgKoAqcCpwKoAqkCqAKqAqkCqQKqAqsCqgKsAqsCqwKsAq0CrAKuAq0CrQKuAq8CrgKwAq8CrwKwArECsAKyArECsQKyArMCsgK0ArMCswK0ArUCtAK2ArUCtQK2ArcCtgK4ArcCtwK4ArkCuAK6ArkCuQK6ArsCugK8ArsCuwK8Ar0CvAK+Ar0CvQK+Ar8CvgLAAr8CvwLAAsECwALCAsECwQLCAsMCwgLEAsMCwwLEAsUCxALGAsUCxQLGAscCxgLIAscCxwLIAskCyALKAskCyQLKAssCygLMAssCywLMAs0CzALOAs0CzQLOAs8CzgLQAs8CzwLQAtEC0ALSAtEC0QLSAtMC0gLUAtMC0wLUAtUC1ALWAtUC1QLWAtcC1gLYAtcC1wLYAtkC2ALaAtkC2QLaAtsC2gLcAtsC2wLcAt0C3ALeAt0C3QLeAt8C3gLgAt8C3wLgAuEC4ALiAuEC4QLiAuMC4gLkAuMC4wLkAuUC5ALmAuUC5QLmAucC5gLoAucC5wLoAukC6ALqAukC6QLqAusC6gLsAusC6wLsAu0C7ALuAu0C7QLuAu8C7gLwAu8C7wLwAvEC8ALyAvEC8QLyAvMC8gL0AvMC8wL0AvUC9AL2AvUC9QL2AvcC9gL4AvcC9wL4AvkC+AL6AvkC+QL6AvsC+gL8AvsC+wL8Av0C/AL+Av0C/QL+Av8C/gIAA/8C/wIAAwEDAAMCAwEDAQMCAwMDAgMEAwMDAwMEAwUDBAMGAwUDBQMGAwcDBgMIAwcDBwMIAwkDCAMKAwkDCQMKAwsDCgMMAwsDCwMMAw0DDAMOAw0DDQMOAw8DDgMQAw8DDwMQAxEDEAMSAxEDEQMSAxMDEgMUAxMDEwMUAxUDFAMWAxUDFQMWAxcDFgMYAxcDFwMYAxkDGAMaAxkDGQMaAxsDGgMcAxsDGwMcAx0DHAMeAx0DHQMeAx8DHgMgAx8DHwMgAyEDIAMiAyEDIQMiAyMDIgMkAyMDIwMkAyUDJAMmAyUDJQMmAycDJgMoAycDJwMoAykDKAMqAykDKQMqAysDKgMsAysDKwMsAy0DLAMuAy0DLQMuAy8DLgMwAy8DLwMwAzEDMAMyAzEDMQMyAzMDMgM0AzMDMwM0AzUDNAM2AzUDNQM2AzcDNgM4AzcDNwM4AzkDOAM6AzkDOQM6AzsDOgM8AzsDOwM8Az0DPAM+Az0DPQM+Az8DPgNAAz8DPwNAA0EDQANCA0EDQQNCA0MDQgNEA0MDQwNEA0UDRANGA0UDRQNGA0cDRgNIA0cDRwNIA0kDSANKA0kDSQNKA0sDSgNMA0sDSwNMA00DTANOA00DTQNOA08DTgNQA08DTwNQA1EDUANSA1EDUQNSA1MDUgNUA1MDUwNUA1UDVANWA1UDVQNWA1cDVgNYA1cDVwNYA1kDWANaA1kDWQNaA1sDWgNcA1sDWwNcA10DXANeA10DXQNeA18DXgNgA18DXwNgA2EDYANiA2EDYQNiA2MDYgNkA2MDYwNkA2UDZANmA2UDZQNmA2cDZgNoA2cDZwNoA2kDaANqA2kDaQNqA2sDagNsA2sDawNsA20DbANuA20DbQNuA28DbgNwA28DbwNwA3EDcANyA3EDcQNyA3MDcgN0A3MDcwN0A3UDdAN2A3UDdQN2A3cDdgN4A3cDdwN4A3kDeAN6A3kDeQN6A3sDegN8A3sDewN8A30DfAN+A30DfQN+A38DfgOAA38DfwOAA4EDgAOCA4EDgQOCA4MDggOEA4MDgwOEA4UDhAOGA4UDhQOGA4cDhgOIA4cDhwOIA4kDiAOKA4kDiQOKA4sDigOMA4sDiwOMA40DjAOOA40DjQOOA48DjgOQA48DjwOQA5EDkAOSA5EDkQOSA5MDkgOUA5MDkwOUA5UDlAOWA5UDlQOWA5cDlgOYA5cDlwOYA5kDmAOaA5kDmQOaA5sDmgOcA5sDmwOcA50DnAOeA50DnQOeA58DngOgA58DnwOgA6EDoAOiA6EDoQOiA6MDogOkA6MDowOkA6UDpAOmA6UDpQOmA6cDpgOoA6cDpwOoA6kDqAOqA6kDqQOqA6sDqgOsA6sDqwOsA60DrAOuA60DrQOuA68DrgOwA68DrwOwA7EDsAOyA7EDsQOyA7MDsgO0A7MDswO0A7UDtAO2A7UDtQO2A7cDtgO4A7cDtwO4A7kDuAO6A7kDuQO6A7sDugO8A7sDuwO8A70DvAO+A70DvQO+A78DvgPAA78DvwPAA8EDwAPCA8EDwQPCA8MDwgPEA8MDwwPEA8UDxAPGA8UDxQPGA8cDxgPIA8cDxwPIA8kDyAPKA8kDyQPKA8sDygPMA8sDywPMA80DzAPOA80DzQPOA88DzgPQA88DzwPQA9ED0APSA9ED0QPSA9MD0gPUA9MD0wPUA9UD1APWA9UD1QPWA9cD1gPYA9cD1wPYA9kD2APaA9kD2QPaA9sD2gPcA9sD2wPcA90D3APeA90D3QPeA98D3gPgA98D3wPgA+ED4APiA+ED4QPiA+MD4gPkA+MD4wPkA+UD5APmA+UD5QPmA+cD5gPoA+cD5wPoA+kD6APqA+kD6QPqA+sD6gPsA+sD6wPsA+0D7APuA+0D7QPuA+8D7gPwA+8D7wPwA/ED8APyA/ED8QPyA/MD8gP0A/MD8wP0A/UD9AP2A/UD9QP2A/cD9gP4A/cD9wP4A/kD+AP6A/kD+QP6A/sD+gP8A/sD+wP8A/0D/AP+A/0D/QP+A/8D/gMABP8D/wMABAEEAAQCBAEEAQQCBAMEAgQEBAMEAwQEBAUEBAQGBAUEBQQGBAcEBgQIBAcEBwQIBAkECAQKBAkECQQKBAsECgQMBAsECwQMBA0EDAQOBA0EDQQOBA8EDgQQBA8EDwQQBBEEEAQSBBEEEQQSBBMEEgQUBBMEEwQUBBUEFAQWBBUEFQQWBBcEFgQYBBcEFwQYBBkEGAQaBBkEGQQaBBsEGgQcBBsEGwQcBB0EHAQeBB0EHQQeBB8EHgQgBB8EHwQgBCEEIAQiBCEEIQQiBCMEIgQkBCMEIwQkBCUEJAQmBCUEJQQmBCcEJgQoBCcEJwQoBCkEKAQqBCkEKQQqBCsEKgQsBCsEKwQsBC0ELAQuBC0ELQQuBC8ELgQwBC8ELwQwBDEEMAQyBDEEMQQyBDMEMgQ0BDMEMwQ0BDUENAQ2BDUENQQ2BDcENgQ4BDcENwQ4BDkEOAQ6BDkEOQQ6BDsEOgQ8BDsEOwQ8BD0EPAQ+BD0EPQQ+BD8EPgRABD8EPwRABEEEQARCBEEEQQRCBEMEQgREBEMEQwREBEUERARGBEUERQRGBEcERgRIBEcERwRIBEkESARKBEkESQRKBEsESgRMBEsESwRMBE0ETAROBE0ETQROBE8ETgRQBE8ETwRQBFEEUARSBFEEUQRSBFMEUgRUBFMEUwRUBFUEVARWBFUEVQRWBFcEVgRYBFcEVwRYBFkEWARaBFkEWQRaBFsEWgRcBFsEWwRcBF0EXAReBF0EXQReBF8EXgRgBF8EXwRgBGEEYARiBGEEYQRiBGMEYgRkBGMEYwRkBGUEZARmBGUEZQRmBGcEZgRoBGcEZwRoBGkEaARqBGkEaQRqBGsEagRsBGsEawRsBG0EbARuBG0EbQRuBG8EbgRwBG8EbwRwBHEEcARyBHEEcQRyBHMEcgR0BHMEcwR0BHUEdAR2BHUEdQR2BHcEdgR4BHcEdwR4BHkEeAR6BHkEeQR6BHsEegR8BHsEewR8BH0EfAR+BH0EfQR+BH8EfgSABH8EfwSABIEEgASCBIEEgQSCBIMEggSEBIMEgwSEBIUEhASGBIUEhQSGBIcEhgSIBIcEhwSIBIkEiASKBIkEiQSKBIsEigSMBIsEiwSMBI0EjASOBI0EjQSOBI8EjgSQBI8EjwSQBJEEkASSBJEEkQSSBJMEkgSUBJMEkwSUBJUElASWBJUElQSWBJcElgSYBJcElwSYBJkEmASaBJkEmQSaBJsEmgScBJsEmwScBJ0EnASeBJ0EnQSeBJ8EngSgBJ8EnwSgBKEEoASiBKEEoQSiBKMEogSkBKMEowSkBKUEpASmBKUEpQSmBKcEpgSoBKcEpwSoBKkEqASqBKkEqQSqBKsEqgSsBKsEqwSsBK0ErASuBK0ErQSuBK8ErgSwBK8ErwSwBLEEsASyBLEEsQSyBLMEsgS0BLMEswS0BLUEtAS2BLUEtQS2BLcEtgS4BLcEtwS4BLkEuAS6BLkEuQS6BLsEugS8BLsEuwS8BL0EvAS+BL0EvQS+BL8EvgTABL8EvwTABMEEwATCBMEEwQTCBMMEwgTEBMMEwwTEBMUExATGBMUExQTGBMcExgTIBMcExwTIBMkEyATKBMkEyQTKBMsEygTMBMsEywTMBM0EzATOBM0EzQTOBM8EzgTQBM8EzwTQBNEE0ATSBNEE0QTSBNME0gTUBNME0wTUBNUE1ATWBNUE1QTWBNcE1gTYBNcE1wTYBNkE2ATaBNkE2QTaBNsE2gTcBNsE2wTcBN0E3ATeBN0E3QTeBN8E3gTgBN8E3wTgBOEE4ATiBOEE4QTiBOME4gTkBOME4wTkBOUE5ATmBOUE5QTmBOcE5gToBOcE5wToBOkE6ATqBOkE6QTqBOsE6gTsBOsE6wTsBO0E7ATuBO0E7QTuBO8E7gTwBO8E7wTwBPEE8ATyBPEE8QTyBPME8gT0BPME8wT0BPUE9AT2BPUE9QT2BPcE9gT4BPcE9wT4BPkE+AT6BPkE+QT6BPsE+gT8BPsE+wT8BP0E/AT+BP0E/QT+BP8E/gQABf8E/wQABQEFAAUCBQEFAQUCBQMFAgUEBQMFAwUEBQUFBAUGBQUFBQUGBQcFBgUIBQcFBwUIBQkFCAUKBQkFCQUKBQsFCgUMBQsFCwUMBQ0FDAUOBQ0FDQUOBQ8FDgUQBQ8FDwUQBREFEAUSBREFEQUSBRMFEgUUBRMFEwUUBRUFFAUWBRUFFQUWBRcFFgUYBRcFFwUYBRkFGAUaBRkFGQUaBRsFGgUcBRsFGwUcBR0FHAUeBR0FHQUeBR8FHgUgBR8FHwUgBSEFIAUiBSEFIQUiBSMFIgUkBSMFIwUkBSUFJAUmBSUFJQUmBScFJgUoBScFJwUoBSkFKAUqBSkFKQUqBSsFKgUsBSsFKwUsBS0FLAUuBS0FLQUuBS8FLgUwBS8FLwUwBTEFMAUyBTEFMQUyBTMFMgU0BTMFMwU0BTUFNAU2BTUFNQU2BTcFNgU4BTcFNwU4BTkFOAU6BTkFOQU6BTsFOgU8BTsFOwU8BT0FPAU+BT0FPQU+BT8FPgVABT8FPwVABUEFQAVCBUEFQQVCBUMFQgVEBUMFQwVEBUUFRAVGBUUFRQVGBUcFRgVIBUcFRwVIBUkFSAVKBUkFSQVKBUsFSgVMBUsFSwVMBU0FTAVOBU0FTQVOBU8FTgVQBU8FTwVQBVEFUAVSBVEFUQVSBVMFUgVUBVMFUwVUBVUFVAVWBVUFVQVWBVcFVgVYBVcFVwVYBVkFWAVaBVkFWQVaBVsFWgVcBVsFWwVcBV0FXAVeBV0FXQVeBV8FXgVgBV8FXwVgBWEFYAViBWEFYQViBWMFYgVkBWMFYwVkBWUFZAVmBWUFZQVmBWcFZgVoBWcFZwVoBWkFaAVqBWkFaQVqBWsFagVsBWsFawVsBW0FbAVuBW0FbQVuBW8FbgVwBW8FbwVwBXEFcAVyBXEFcQVyBXMFcgV0BXMFcwV0BXUFdAV2BXUFdQV2BXcFdgV4BXcFdwV4BXkFeAV6BXkFeQV6BXsFegV8BXsFewV8BX0FfAV+BX0FfQV+BX8FfgWABX8FfwWABYEFgAWCBYEFgQWCBYMFggWEBYMFgwWEBYUFhAWGBYUFhQWGBYcFhgWIBYcFhwWIBYkFiAWKBYkFiQWKBYsFigWMBYsFiwWMBY0FjAWOBY0FjQWOBY8FjgWQBY8FjwWQBZEFkAWSBZEFkQWSBZMFkgWUBZMFkwWUBZUFlAWWBZUFlQWWBZcFlgWYBZcFlwWYBZkFmAWaBZkFmQWaBZsFmgWcBZsFmwWcBZ0FnAWeBZ0FnQWeBZ8FngWgBZ8FnwWgBaEFoAWiBaEFoQWiBaMFogWkBaMFowWkBaUFpAWmBaUFpQWmBacFpgWoBacFpwWoBakFqAWqBakFqQWqBasFqgWsBasFqwWsBa0FrAWuBa0FrQWuBa8FrgWwBa8FrwWwBbEFsAWyBbEFsQWyBbMFsgW0BbMFswW0BbUFtAW2BbUFtQW2BbcFtgW4BbcFtwW4BbkFuAW6BbkFuQW6BbsFugW8BbsFuwW8Bb0FvAW+Bb0FvQW+Bb8FvgXABb8FvwXABcEFwAXCBcEFwQXCBcMFwgXEBcMFwwXEBcUFxAXGBcUFxQXGBccFxgXIBccFxwXIBckFyAXKBckFyQXKBcsFygXMBcsFywXMBc0FzAXOBc0FzQXOBc8FzgXQBc8FzwXQBdEF0AXSBdEF0QXSBdMF0gXUBdMF0wXUBdUF1AXWBdUF1QXWBdcF1gXYBdcF1wXYBdkF2AXaBdkF2QXaBdsF2gXcBdsF2wXcBd0F3AXeBd0F3QXeBd8F3gXgBd8F3wXgBeEF4AXiBeEF4QXiBeMF4gXkBeMF4wXkBeUF5AXmBeUF5QXmBecF5gXoBecF5wXoBekF6AXqBekF6QXqBesF6gXsBesF6wXsBe0F7AXuBe0F7QXuBe8F7gXwBe8F7wXwBfEF8AXyBfEF8QXyBfMF8gX0BfMF8wX0BfUF9AX2BfUF9QX2BfcF9gX4BfcF9wX4BfkF+AX6BfkF+QX6BfsF+gX8BfsF+wX8Bf0F/AX+Bf0F/QX+Bf8F/gUABv8F/wUABgEGAAYCBgEGAQYCBgMGAgYEBgMGAwYEBgUGBAYGBgUGBQYGBgcGBgYIBgcGBwYIBgkGCAYKBgkGCQYKBgsGCgYMBgsGCwYMBg0GDAYOBg0GDQYOBg8GDgYQBg8GDwYQBhEGEAYSBhEGEQYSBhMGEgYUBhMGEwYUBhUGFAYWBhUGFQYWBhcGFgYYBhcGFwYYBhkGGAYaBhkGGQYaBhsGGgYcBhsGGwYcBh0GHAYeBh0GHQYeBh8GHgYgBh8GHwYgBiEGIAYiBiEGIQYiBiMGIgYkBiMGIwYkBiUGJAYmBiUGJQYmBicGJgYoBicGJwYoBikGKAYqBikGKQYqBisGKgYsBisGKwYsBi0GLAYuBi0GLQYuBi8GLgYwBi8GLwYwBjEGMAYyBjEGMQYyBjMGMgY0BjMGMwY0BjUGNAY2BjUGNQY2BjcGNgY4BjcGNwY4BjkGOAY6BjkGOQY6BjsGOgY8BjsGOwY8Bj0GPAY+Bj0GPQY+Bj8GPgZABj8GPwZABkEGQAZCBkEGQQZCBkMGQgZEBkMGQwZEBkUGRAZGBkUGRQZGBkcGRgZIBkcGRwZIBkkGSAZKBkkGSQZKBksGSgZMBksGSwZMBk0GTAZOBk0GTQZOBk8GTgZQBk8GTwZQBlEGUAZSBlEGUQZSBlMGUgZUBlMGUwZUBlUGVAZWBlUGVQZWBlcGVgZYBlcGVwZYBlkGWAZaBlkGWQZaBlsGWgZcBlsGWwZcBl0GXAZeBl0GXQZeBl8GXgZgBl8GXwZgBmEGYAZiBmEGYQZiBmMGYgZkBmMGYwZkBmUGZAZmBmUGZQZmBmcGZgZoBmcGZwZoBmkGaAZqBmkGaQZqBmsGagZsBmsGawZsBm0GbAZuBm0GbQZuBm8GbgZwBm8GbwZwBnEGcAZyBnEGcQZyBnMGcgZ0BnMGcwZ0BnUGdAZ2BnUGdQZ2BncGdgZ4BncGdwZ4BnkGeAZ6BnkGeQZ6BnsGegZ8BnsGewZ8Bn0GfAZ+Bn0GfQZ+Bn8GfgaABn8GfwaABoEGgAaCBoEGgQaCBoMGggaEBoMGgwaEBoUGhAaGBoUGhQaGBocGhgaIBocGhwaIBokGiAaKBokGiQaKBosGigaMBosGiwaMBo0GjAaOBo0GjQaOBo8GjgaQBo8GjwaQBpEGkAaSBpEGkQaSBpMGkgaUBpMGkwaUBpUGlAaWBpUGlQaWBpcGlgaYBpcGlwaYBpkGmAaaBpkGmQaaBpsGmgacBpsGmwacBp0GnAaeBp0GnQaeBp8GngagBp8GnwagBqEGoAaiBqEGoQaiBqMGogakBqMGowakBqUGpAamBqUGpQamBqcGpgaoBqcGpwaoBqkGqAaqBqkGqQaqBqsGqgasBqsGqwasBq0GrAauBq0GrQauBq8GrgawBq8GrwawBrEGsAayBrEGsQayBrMGsga0BrMGswa0BrUGtAa2BrUGtQa2BrcGtga4BrcGtwa4BrkGuAa6BrkGuQa6BrsGuga8BrsGuwa8Br0GvAa+Br0GvQa+Br8GvgbABr8GvwbABsEGwAbCBsEGwQbCBsMGwgbEBsMGwwbEBsUGxAbGBsUGxQbGBscGxgbIBscGxwbIBskGyAbKBskGyQbKBssGygbMBssGywbMBs0GzAbOBs0GzQbOBs8GzgbQBs8GzwbQBtEG0AbSBtEG0QbSBtMG0gbUBtMG0wbUBtUG1AbWBtUG1QbWBtcG1gbYBtcG1wbYBtkG2AbaBtkG2QbaBtsG2gbcBtsG2wbcBt0G3AbeBt0G3QbeBt8G3gbgBt8G3wbgBuEG4AbiBuEG4QbiBuMG4gbkBuMG4wbkBuUG5AbmBuUG5QbmBucG5gboBucG5wboBukG6AbqBukG6QbqBusG6gbsBusG6wbsBu0G7AbuBu0G7QbuBu8G7gbwBu8G7wbwBvEG8AbyBvEG8QbyBvMG8gb0BvMG8wb0BvUG9Ab2BvUG9Qb2BvcG9gb4BvcG9wb4BvkG+Ab6BvkG+Qb6BvsG+gb8BvsG+wb8Bv0G/Ab+Bv0G/Qb+Bv8G/gYAB/8G/wYABwEHAAcCBwEHAQcCBwMHAgcEBwMHAwcEBwUHBAcGBwUHBQcGBwcHBgcIBwcHBwcIBwkHCAcKBwkHCQcKBwsHCgcMBwsHCwcMBw0HDAcOBw0HDQcOBw8HDgcQBw8HDwcQBxEHEAcSBxEHEQcSBxMHEgcUBxMHEwcUBxUHFAcWBxUHFQcWBxcHFgcYBxcHFwcYBxkHGAcaBxkHGQcaBxsHGgccBxsHGwccBx0HHAceBx0HHQceBx8HHgcgBx8HHwcgByEHIAciByEHIQciByMHIgckByMHIwckByUHJAcmByUHJQcmBycHJgcoBycHJwcoBykHKAcqBykHKQcqBysHKgcsBysHKwcsBy0HLAcuBy0HLQcuBy8HLgcwBy8HLwcwBzEHMAcyBzEHMQcyBzMHMgc0BzMHMwc0BzUHNAc2BzUHNQc2BzcHNgc4BzcHNwc4BzkHOAc6BzkHOQc6BzsHOgc8BzsHOwc8Bz0HPAc+Bz0HPQc+Bz8HPgdABz8HPwdAB0EHQAdCB0EHQQdCB0MHQgdEB0MHQwdEB0UHRAdGB0UHRQdGB0cHRgdIB0cHRwdIB0kHSAdKB0kHSQdKB0sHSgdMB0sHSwdMB00HTAdOB00HTQdOB08HTgdQB08HTwdQB1EHUAdSB1EHUQdSB1MHUgdUB1MHUwdUB1UHVAdWB1UHVQdWB1cHVgdYB1cHVwdYB1kHWAdaB1kHWQdaB1sHWgdcB1sHWwdcB10HXAdeB10HXQdeB18HXgdgB18HXwdgB2EHYAdiB2EHYQdiB2MHYgdkB2MHYwdkB2UHZAdmB2UHZQdmB2cHZgdoB2cHZwdoB2kHaAdqB2kHaQdqB2sHagdsB2sHawdsB20HbAduB20HbQduB28HbgdwB28HbwdwB3EHcAdyB3EHcQdyB3MHcgd0B3MHcwd0B3UHdAd2B3UHdQd2B3cHdgd4B3cHdwd4B3kHeAd6B3kHeQd6B3sHegd8B3sHewd8B30HfAd+B30HfQd+B38HfgeAB38HfweAB4EHgAeCB4EHgQeCB4MHggeEB4MHgweEB4UHhAeGB4UHhQeGB4cHhgeIB4cHhweIB4kHiAeKB4kHiQeKB4sHigeMB4sHiweMB40HjAeOB40HjQeOB48HjgeQB48HjweQB5EHkAeSB5EHkQeSB5MHkgeUB5MHkweUB5UHlAeWB5UHlQeWB5cHlgeYB5cHlweYB5kHmAeaB5kHmQeaB5sHmgecB5sHmwecB50HnAeeB50HnQeeB58HngegB58HnwegB6EHoAeiB6EHoQeiB6MHogekB6MHowekB6UHpAemB6UHpQemB6cHpgeoB6cHpweoB6kHqAeqB6kHqQeqB6sHqgesB6sHqwesB60HrAeuB60HrQeuB68HrgewB68HrwewB7EHsAeyB7EHsQeyB7MHsge0B7MHswe0B7UHtAe2B7UHtQe2B7cHtge4B7cHtwe4B7kHuAe6B7kHuQe6B7sHuge8B7sHuwe8B70HvAe+B70HvQe+B78HvgfAB78HvwfAB8EHwAfCB8EHwQfCB8MHwgfEB8MHwwfEB8UHxAfGB8UHxQfGB8cHxgfIB8cHxwfIB8kHyAfKB8kHyQfKB8sHygfMB8sHywfMB80HzAfOB80HzQfOB88HzgfQB88HzwfQB9EH0AfSB9EH0QfSB9MH0gfUB9MH0wfUB9UH1AfWB9UH1QfWB9cH1gfYB9cH1wfYB9kH2AfaB9kH2QfaB9sH2gfcB9sH2wfcB90H3AfeB90H3QfeB98H3gfgB98H3wfgB+EH4AfiB+EH4QfiB+MH4gfkB+MH4wfkB+UH5AfmB+UH5QfmB+cH5gfoB+cH5wfoB+kH6AfqB+kH6QfqB+sH6gfsB+sH6wfsB+0H7AfuB+0H7QfuB+8H7gfwB+8H7wfwB/EH8AfyB/EH8QfyB/MH8gf0B/MH8wf0B/UH9Af2B/UH9Qf2B/cH9gf4B/cH9wf4B/kH+Af6B/kH+Qf6B/sH+gf8B/sH+wf8B/0H/Af+B/0H/Qf+B/8H/gcACP8H/wcACAEIAAgCCAEIAQgCCAMIAggECAMIAwgECAUIBAgGCAUIBQgGCAcIBggICAcIBwgICAkICAgKCAkICQgKCAsICggMCAsICwgMCA0IDAgOCA0IDQgOCA8IDggQCA8IDwgQCBEIEAgSCBEIEQgSCBMIEggUCBMIEwgUCBUIFAgWCBUIFQgWCBcIFggYCBcIFwgYCBkIGAgaCBkIGQgaCBsIGggcCBsIGwgcCB0IHAgeCB0IHQgeCB8IHgggCB8IHwggCCEIIAgiCCEIIQgiCCMIIggkCCMIIwgkCCUIJAgmCCUIJQgmCCcIJggoCCcIJwgoCCkIKAgqCCkIKQgqCCsIKggsCCsIKwgsCC0ILAguCC0ILQguCC8ILggwCC8ILwgwCDEIMAgyCDEIMQgyCDMIMgg0CDMIMwg0CDUINAg2CDUINQg2CDcINgg4CDcINwg4CDkIOAg6CDkIOQg6CDsIOgg8CDsIOwg8CD0IPAg+CD0IPQg+CD8IPghACD8IPwhACEEIQAhCCEEIQQhCCEMIQghECEMIQwhECEUIRAhGCEUIRQhGCEcIRghICEcIRwhICEkISAhKCEkISQhKCEsISghMCEsISwhMCE0ITAhOCE0ITQhOCE8ITghQCE8ITwhQCFEIUAhSCFEIUQhSCFMIUghUCFMIUwhUCFUIVAhWCFUIVQhWCFcIVghYCFcIVwhYCFkIWAhaCFkIWQhaCFsIWghcCFsIWwhcCF0IXAheCF0IXQheCF8IXghgCF8IXwhgCGEIYAhiCGEIYQhiCGMIYghkCGMIYwhkCGUIZAhmCGUIZQhmCGcIZghoCGcIZwhoCGkIaAhqCGkIaQhqCGsIaghsCGsIawhsCG0IbAhuCG0IbQhuCG8IbghwCG8IbwhwCHEIcAhyCHEIcQhyCHMIcgh0CHMIcwh0CHUIdAh2CHUIdQh2CHcIdgh4CHcIdwh4CHkIeAh6CHkIeQh6CHsIegh8CHsIewh8CH0IfAh+CH0IfQh+CH8IfgiACH8IfwiACIEIgAiCCIEIgQiCCIMIggiECIMIgwiECIUIhAiGCIUIhQiGCIcIhgiICIcIhwiICIkIiAiKCIkIiQiKCIsIigiMCIsIiwiMCI0IjAiOCI0IjQiOCI8IjgiQCI8IjwiQCJEIkAiSCJEIkQiSCJMIkggAAJMIkwgAAAEA"), "primitive": 3, "uv_scale": Vector4(0, 0, 0, 0), -"vertex_count": 1812, -"vertex_data": PackedByteArray("Rr6cQRsv3b5fSszBPiejQRsv3b5WBcrBc/SbQRsv3b5g28rBy+6hQRsv3b6qncfBnR2bQRsv3b4fT8nB9RehQRsv3b5pEcbBxkaaQRsv3b7ewsfBHkGgQRsv3b4ohcTBGW6ZQRsv3b4kM8bBFHifQRsv3b7vEsPBGaSYQRsv3b7nmcTBF8SeQRsv3b68pcHBNOOXQRsv3b50AMPBAgqeQRsv3b6VGsDB6iKXQRsv3b5IaMHBW0udQRsv3b7jhb7BNmGWQRsv3b74x7/B8qacQRsv3b6nJ73BpMCVQRsv3b4wGr7Bjh+cQRsv3b6AubvBiSKVQRsv3b6KcrzBtIWbQRsv3b5gHbrB1YaUQRsv3b6mvbrBpQ2bQRsv3b4B1bjBzR6UQRsv3b7E+rjBVr+aQRsv3b6wdLfBcbaTQRsv3b4SMbfBgHOaQRsv3b58R7bBYKGTQRsv3b7rU7XBJm6aQRsv3b5PXbXBmcWTQRsv3b6MVLPBtyqaQRsv3b5apLXBo/yUQRsv3b6GorHBsO2ZQRsv3b5dTrbBAHeWQRsv3b58bLDBfwqaQRsv3b4dNbbBSjOYQRsv3b7Yoa/BtaiaQRsv3b7f+LXBFfeZQRsv3b5hD6/BwYabQRsv3b6trbXBPsWbQRsv3b7yyK7B+8icQRsv3b5CgrXB3YqdQRsv3b6ohK7BUUueQRsv3b7JRrXBglqfQRsv3b5WY67BzpifQRsv3b4FL7XBlSShQRsv3b40Y67BlSShQRsv3b4BMLXBVOeiQRsv3b40Y67BVOeiQRsv3b4BMLXBaJKkQRsv3b6TZK7BklulQRsv3b63JbXBASSmQRsv3b4bBK7B9QCoQRsv3b5LjrTB+ManQRsv3b4+eK3BdO2pQRsv3b6267PBdlOpQRsv3b4I96zBgIKsQRsv3b4++bLBa4mqQRsv3b4XBazBZgevQRsv3b74H7HBlb2rQRsv3b5Z0KrBXpWwQRsv3b5clq/BddmsQRsv3b6Ks6nBMKmyQRsv3b5xO63BvFStQRsv3b6BMajBNMizQRsv3b79V6rB+uKtQRsv3b7FhqbB7li0QRsv3b6+pajBs2WuQRsv3b6B+qTBTR21QRsv3b5XCabBNGOuQRsv3b6fV6PBATC1QRsv3b6fV6PBNGOuQRsv3b7glKHBATC1QRsv3b7glKHBPGOuQRsv3b6ezp/Bxy+1QRsv3b527J/BgnOuQRsv3b61/Z3B7ji1QRsv3b7QnZ7BMLiuQRsv3b5INJzBgHG1QRsv3b4FOJ3BIP2uQRsv3b79bJrBJKi1QRsv3b5JwpvBG3GvQRsv3b4yoJjBzNy1QRsv3b583ZrBszewQRsv3b6Q6JbBYje2QRsv3b5bHJrB0D6xQRsv3b6VSJXBlDm2QRsv3b4P6pnBsuqyQRsv3b57DpTBn721QRsv3b4SPprBjs+0QRsv3b54opPBiYO1QRsv3b7xZZrBM5O2QRsv3b5hopPBjj+3QRsv3b6gZprBNze4QRsv3b7iTJPB95i5QRsv3b5c9ZnBwOa5QRsv3b618JLBAJe7QRsv3b79hpnBoYm7QRsv3b4Sc5LBLaG9QRsv3b5x65jBPS+9QRsv3b6Y4JHBW2q/QRsv3b4LTZjBgtO+QRsv3b65TpHBnT3BQRsv3b4YqpfBXWzAQRsv3b7CpZDBJhbDQRsv3b5+55bBLwfCQRsv3b6r8Y/BDsLEQRsv3b4FLJbBQqPDQRsv3b4JPY/BemTGQRsv3b6YdJXBhjnFQRsv3b4kh47BhyXIQRsv3b4Lq5TBuMfGQRsv3b40vo3Bx9fJQRsv3b5v0JPBKVrIQRsv3b4v84zBOGrLQRsv3b5qBZPBmezJQRsv3b4rKIzBqfzMQRsv3b5mOpLBZUXLQRsv3b5wiIvBaT/QQRsv3b64KpDBbb7LQRsv3b7AOYrBmyXSQRsv3b7Pg4zBiULMQRsv3b5Yk4jBx7/SQRsv3b6Zm4rBP8nMQRsv3b4z5YbBfUbTQRsv3b517YjB9U/NQRsv3b4NN4XBM83TQRsv3b5OP4fBqtbNQRsv3b7oiIPB6FPUQRsv3b4pkYXBYF3OQRsv3b7D2oHBntrUQRsv3b4F44PBFuTOQRsv3b6dLIDBVGHVQRsv3b7eNILBy2rPQRsv3b7w/HzBCejVQRsv3b65hoDBmO/PQRsv3b6crXnBGH3WQRsv3b7tT33B5VTQQRsv3b5hanbBnAbXQRsv3b4oznjBU4fQQRsv3b53A3PBZUjXQRsv3b71lnTBkLvQQRsv3b4MhG/BonzXQRsv3b6KF3HBze/QQRsv3b6hBGzB37DXQRsv3b4fmG3BCiTRQRsv3b42hWjBHOXXQRsv3b60GGrBR1jRQRsv3b7LBWXBWRnYQRsv3b5JmWbBhIzRQRsv3b5ghmHBlk3YQRsv3b7eGWPBwcDRQRsv3b71Bl7B04HYQRsv3b5zml/B/vTRQRsv3b6Kh1rBELbYQRsv3b4IG1zBOynSQRsv3b4fCFfBTerYQRsv3b6dm1jBeF3SQRsv3b60iFPBih7ZQRsv3b4yHFXBtZHSQRsv3b5JCVDBx1LZQRsv3b7HnFHB8sXSQRsv3b7eiUzBBIfZQRsv3b5cHU7BL/rSQRsv3b5zCknBQbvZQRsv3b7xnUrBbC7TQRsv3b4Ii0XBfu/ZQRsv3b6GHkfBqWLTQRsv3b6dC0LBuyPaQRsv3b4bn0PBLZbTQRsv3b73mj7Ba13aQRsv3b7qsD/BYqnTQRsv3b7pJTvBpHTaQRsv3b5UuDvBmbvTQRsv3b7QoTfB+YbaQRsv3b6kLjjB1c3TQRsv3b4PHTTBNZnaQRsv3b7jqTTBEODTQRsv3b5OmDDBcKvaQRsv3b4iJTHBTPLTQRsv3b6NEy3BrL3aQRsv3b5hoC3BhwTUQRsv3b7LjinB58/aQRsv3b6bGyrBwhbUQRsv3b4KCibBIuLaQRsv3b7elibB/ijUQRsv3b5JhSLBXvTaQRsv3b4dEiPBOTvUQRsv3b6HAB/BmQbbQRsv3b5bjR/BdU3UQRsv3b7GexvB1RjbQRsv3b6aCBzBsF/UQRsv3b4F9xfBECvbQRsv3b7VgxjB63HUQRsv3b5DchTBSz3bQRsv3b4X/xTBJ4TUQRsv3b6C7RDBh0/bQRsv3b5WehHBYpbUQRsv3b7BaA3BwmHbQRsv3b6V9Q3BnqjUQRsv3b4A5AnB/nPbQRsv3b7UcArB2brUQRsv3b4/XwbBOYbbQRsv3b4P7AbBFM3UQRsv3b592gLBdJjbQRsv3b5RZwPBUN/UQRsv3b53q/7AsKrbQRsv3b4fxf/Ai/HUQRsv3b71offA67zbQRsv3b6du/jAxwPVQRsv3b5ymPDAJ8/bQRsv3b4asvHAAhbVQRsv3b7wjunAYuHbQRsv3b6YqOrAPijVQRsv3b5theLAnvPbQRsv3b4Vn+PAZzrVQRsv3b6ug9vAXgbcQRsv3b5bW9zAAUTVQRsv3b4MitTAtBDcQRsv3b7j1NTA/kPVQRsv3b7eh83AyxDcQRsv3b7eh83A/kPVQRsv3b7ifMbAyxDcQRsv3b7ifMbA/kPVQRsv3b7lcb/AyxDcQRsv3b7lcb/A/kPVQRsv3b7pZrjAyxDcQRsv3b7pZrjA/kPVQRsv3b7tW7HAyxDcQRsv3b7tW7HA/kPVQRsv3b7wUKrAyxDcQRsv3b7wUKrA/kPVQRsv3b70RaPAyxDcQRsv3b70RaPA/kPVQRsv3b74OpzAyxDcQRsv3b74OpzA/kPVQRsv3b78L5XAyxDcQRsv3b78L5XA/kPVQRsv3b4AJY7AyxDcQRsv3b4AJY7A/kPVQRsv3b4EGofAyxDcQRsv3b4EGofA/kPVQRsv3b4HD4DAyxDcQRsv3b4HD4DA/kPVQRsv3b4WCHLAyxDcQRsv3b4WCHLA/kPVQRsv3b4d8mPAyxDcQRsv3b4d8mPA/kPVQRsv3b4k3FXAyxDcQRsv3b4k3FXA/kPVQRsv3b4sxkfAyxDcQRsv3b4sxkfA/kPVQRsv3b4zsDnAyxDcQRsv3b4zsDnA/kPVQRsv3b46mivAyxDcQRsv3b46mivA/kPVQRsv3b5EhB3AyxDcQRsv3b5EhB3A/kPVQRsv3b5Kbg/AyxDcQRsv3b5Kbg/A/kPVQRsv3b5SWAHAyxDcQRsv3b5SWAHA/kPVQRsv3b60hOa/yxDcQRsv3b60hOa//kPVQRsv3b7EWMq/yxDcQRsv3b7EWMq//kPVQRsv3b7QLK6/yxDcQRsv3b7QLK6//kPVQRsv3b7gAJK/yxDcQRsv3b7gAJK//kPVQRsv3b7gqWu/yxDcQRsv3b7gqWu//kPVQRsv3b74UTO/yxDcQRsv3b74UTO//kPVQRsv3b4w9PW+yxDcQRsv3b4w9PW+/kPVQRsv3b5wRIW+yxDcQRsv3b5wRIW+AUTVQRsv3b6mlii9uRDcQRsv3b6fEAe9XDvVQRsv3b7n9jU+wwfcQRsv3b5UoUg+uDDVQRsv3b6Khss+CP3bQRsv3b4Bz9U+EibVQRsv3b4gFx4/YvLbQRsv3b5bOyM/axvVQRsv3b70alY/u+fbQRsv3b5tj1s/xBDVQRsv3b5rX4c/FN3bQRsv3b6J8Yk/HgbVQRsv3b5YiaM/btLbQRsv3b52G6Y/d/vUQRsv3b5Cs78/x8fbQRsv3b5/RcI/0PDUQRsv3b403ds/IL3bQRsv3b5Sb94/KubUQRsv3b4gB/g/erLbQRsv3b4+mfo/g9vUQRsv3b6GGApA06fbQRsv3b6kYQtA3NDUQRsv3b58LRhALJ3bQRsv3b6adhlANcbUQRsv3b51QiZAhZLbQRsv3b6DiydAj7vUQRsv3b5sVzRA34fbQRsv3b56oDVA6LDUQRsv3b5gbEJAOH3bQRsv3b5+tUNAQabUQRsv3b5ZgVBAkXLbQRsv3b5nylFAm5vUQRsv3b5Qll5A62fbQRsv3b5e319A9JDUQRsv3b5Fq2xARF3bQRsv3b5j9G1ATYbUQRsv3b4+wHpAnVLbQRsv3b5MCXxAp3vUQRsv3b6aaoRA90fbQRsv3b4hD4VAAHHUQRsv3b4UdYtAUD3bQRsv3b6jGYxAWWbUQRsv3b6Qf5JAqTLbQRsv3b4fJJNAslvUQRsv3b4MiplAAijbQRsv3b6TLppAG1HUQRsv3b5WjKBA7BzbQRsv3b58dqFApz3UQRsv3b7AgqdAtgfbQRsv3b5ZCalA6h7UQRsv3b5Lf65AnufaQRsv3b6eXLBABADUQRsv3b4IhrVAuMjaQRsv3b5jY7dAHeHTQRsv3b7GjLxA0anaQRsv3b4har5AN8LTQRsv3b6Gk8NA64raQRsv3b7ZcMVAUaPTQRsv3b5DmspABWzaQRsv3b6ed8xAaoTTQRsv3b4CodFAHk3aQRsv3b5dftNAhGXTQRsv3b7Bp9hAOC7aQRsv3b4UhdpAnkbTQRsv3b5/rt9AUg/aQRsv3b7ai+FAtyfTQRsv3b49teZAa/DZQRsv3b6YkuhA0QjTQRsv3b78u+1AhdHZQRsv3b5Pme9A6+nSQRsv3b66wvRAn7LZQRsv3b4VoPZABMvSQRsv3b55yftAuJPZQRsv3b7Upv1AHqzSQRsv3b4caAFB0nTZQRsv3b7GVgJBOI3SQRsv3b576wRB7FXZQRsv3b4o2gVBoW7SQRsv3b7VZghBqDTZQRsv3b6qmQlBlD/SQRsv3b64zQtBjfnYQRsv3b5fzA1BXe3RQRsv3b6/Mw9B/5vYQRsv3b4VuRFB0ZnRQRsv3b6eqRJBc0jYQRsv3b70LhVBREbRQRsv3b59HxZB5vTXQRsv3b7XpBhBt/LQQRsv3b5blRlBWaHXQRsv3b62GhxBKp/QQRsv3b46Cx1BzE3XQRsv3b6QkB9BnkvQQRsv3b4agSBBQPrWQRsv3b5wBiNBEfjPQRsv3b739iNBs6bWQRsv3b5SfCZBVazPQRsv3b4yMidBghjWQRsv3b7/qStB5RDPQRsv3b7/gSlBZxDUQRsv3b63ujJBSb/NQRsv3b4TPStB7o3RQRsv3b7UgTZBM17MQRsv3b6SHS1Bkn/PQRsv3b5TMDlB7NrKQRsv3b64Py5B5PvMQRsv3b5RKjtBjDHJQRsv3b4xMC9BvwvLQRsv3b4hRjxB2H/HQRsv3b7DJTBBC1rJQRsv3b6zOz1BGdfFQRsv3b6yFjFBNmXHQRsv3b4HVD5B1yvEQRsv3b7CtjFBZz3FQRsv3b4aJT9BgXLCQRsv3b4vKjJBhlTDQRsv3b5Qpj9Bk7PAQRsv3b47nzJBmJXBQRsv3b5cG0BBpPS+QRsv3b5HFDNBqda/QRsv3b5okEBBtjW9QRsv3b5TiTNBuxe+QRsv3b50BUFB5H27QRsv3b7D/DNBdyO8QRsv3b6UhkFBTsS5QRsv3b7VMTRBVCS6QRsv3b4ixkFBKwO4QRsv3b7aXjRBUVq4QRsv3b4W9EFB/UC2QRsv3b77izRBIpi2QRsv3b43IUJBzn60QRsv3b4cuTRB9NW0QRsv3b5YTkJBoLyyQRsv3b4+5jRBxhOzQRsv3b56e0JBcvqwQRsv3b5fEzVBl1GxQRsv3b6bqEJBaDqvQRsv3b5PQDVBVX+vQRsv3b4t10JBHn2tQRsv3b5GWjVBdpatQRsv3b6B80JBWr2rQRsv3b46WjVBWr2rQRsv3b7U80JBm/qpQRsv3b46WjVBm/qpQRsv3b7U80JB3DeoQRsv3b46WjVB3DeoQRsv3b7U80JBHXWmQRsv3b46WjVBHXWmQRsv3b7U80JBXrKkQRsv3b46WjVBXrKkQRsv3b7U80JBn++iQRsv3b46WjVBn++iQRsv3b7U80JB4CyhQRsv3b46WjVB4CyhQRsv3b7U80JBVGifQRsv3b4+WjVBoHefQRsv3b6280JBBZ6dQRsv3b6FajVBNPCdQRsv3b49AENBi9abQRsv3b5/sDVBN1ycQRsv3b7RP0NBIRWaQRsv3b649TVBzZqaQRsv3b4KhUNBt1OYQRsv3b7xOjZBY9mYQRsv3b5DykNBTJKWQRsv3b4qgDZB+BeXQRsv3b58D0RB4tCUQRsv3b5jxTZBjlaVQRsv3b61VERB7w6TQRsv3b6yCjdBKpmTQRsv3b5NmURBLUCRQRsv3b65VzdBrz6SQRsv3b7qy0RBcXqPQRsv3b6FGThBk+eQQRsv3b6sZUVBucGNQRsv3b6c1jhB2y6PQRsv3b7DIkZBAQmMQRsv3b6ykzlBI3aNQRsv3b7Z30ZBSVCKQRsv3b7JUDpBa72LQRsv3b7wnEdBeIKIQRsv3b5OGTtB5qKKQRsv3b5CBEhBatCGQRsv3b5qoDxB0M+JQRsv3b531UhBhTyFQRsv3b49TD5BmWuIQRsv3b6fUEpBt6GDQRsv3b7CAEBBNzuHQRsv3b6MiktBrhGCQRsv3b6xVkJB5keHQRsv3b77EktB5S2BQRsv3b7wzEVB7EKHQRsv3b7q4UtBKl+AQRsv3b6JCUlBCKCGQRsv3b4tYU5B3YN/QRsv3b5cdkxBQDOGQRsv3b4m0FBBSnB+QRsv3b5d2U9BWLSFQRsv3b6A8FNBIFx9QRsv3b5NTlNB60eFQRsv3b5vklZB7rN8QRsv3b5F3VZBFgqFQRsv3b6dUllB+RB8QRsv3b7qU1pBnLiEQRsv3b5CyVxB2Wp7QRsv3b6L3l1BAXOEQRsv3b73qV9B/B17QRsv3b5gdWFB9FWEQRsv3b5ckmJBCdV6QRsv3b5Q+GRBnTGEQRsv3b7qEWZBeIt6QRsv3b4/hGhBnA+EQRsv3b7+TWlBWWt6QRsv3b4KGWxBNQKEQRsv3b7jVWxBSWt6QRsv3b6xpW9BcgKEQRsv3b6xpW9BSWt6QRsv3b4vK3NBcgKEQRsv3b4vK3NBkmt6QRsv3b55oXZBYwGEQRsv3b6xIndB8Cl6QRsv3b79D3pBbNuDQRsv3b4OOXtBbtN5QRsv3b68jH1BZ66DQRsv3b5w3H5BSIF5QRsv3b7ndYBBBXGDQRsv3b4xr4FBu514QRsv3b6iFYJBqNqCQRsv3b7r7INB68F3QRsv3b6anoNBvOWBQRsv3b7KyIZBEhB2QRsv3b5croRBxKV9QRsv3b5MU4pB53NzQRsv3b45AoVBQLJ2QRsv3b7KnItBKOBvQRsv3b6jZYVBXLt0QRsv3b6sv4tBeXtsQRsv3b6jXYZBKJF0QRsv3b501YtB7KxpQRsv3b6LrIdBQtpzQRsv3b5KL4xBM3JnQRsv3b7mMYlBYudyQRsv3b6324xB76xlQRsv3b7syYpBKLVxQRsv3b5d9Y1BeCFkQRsv3b4gVoxBX+hvQRsv3b6+vI9BZ0piQRsv3b59uo1BTjZtQRsv3b7Zx5FB/TlgQRsv3b4L+o5BzKhpQRsv3b4P4JNBK+VdQRsv3b5F449BSrFjQRsv3b79CZZBM8RaQRsv3b7GOZBBcr9eQRsv3b5PupZBOEBXQRsv3b5+6pBBtfBdQRsv3b4n1pZBeTpUQRsv3b5CEZJB7whdQRsv3b7PP5dBAohRQRsv3b5sVZNBq29bQRsv3b5Q/pdBKTdPQRsv3b75uZRBMLFZQRsv3b71D5lB2vBMQRsv3b7SGpZBPrJXQRsv3b47RJpBztZKQRsv3b7ijZdB6OZVQRsv3b5lgptBKMxIQRsv3b5RAJlBpPVTQRsv3b7Y4pxBFsNGQRsv3b77e5pB1UlSQRsv3b7rF55BefxEQRsv3b42C5xBt85QQRsv3b7wZ59BOzVDQRsv3b7bm51BPVhPQRsv3b5braBB57tBQRsv3b7lQp9BIT1OQRsv3b5P76FB1FpAQRsv3b5Z8qBBBzpNQRsv3b5QJKNB6GI/QRsv3b5ltKJBjpVMQRsv3b6XWKRBT5U+QRsv3b6jhaRBrxZMQRsv3b6hUqVBg4Q+QRsv3b47U6ZBqRdMQRsv3b4pvaZBxy4+QRsv3b6v76dBC3pLQRsv3b7gXqlBZCo9QRsv3b5BealBI79JQRsv3b5fDqxBvbE7QRsv3b4y86pBvotHQRsv3b4PSa5BoeU5QRsv3b5iX6xBxApFQRsv3b4HRbBBt9Y3QRsv3b6PtK1B+kFCQRsv3b5oE7JBeIQ1QRsv3b6z+q5BbYQ/QRsv3b6MlrNBny8zQRsv3b7nOrBBApg8QRsv3b4BJLVBwpMwQRsv3b5WWLFB2Ws5QRsv3b7IgrZBDOstQRsv3b62e7JBA6A2QRsv3b79tLdBIkMrQRsv3b7Dj7NB41MzQRsv3b5mCblByWQoQRsv3b5fhLRBi/AvQRsv3b6iLLpBWnglQRsv3b7zfbVBzvYsQRsv3b6gKrtBoZciQRsv3b4Eb7ZBL4EpQRsv3b4zSrxBd4sfQRsv3b6CPbdB0eklQRsv3b6mP71B7G4cQRsv3b6VELhBQ80iQRsv3b65Er5BvFwZQRsv3b4A4bhBDWMfQRsv3b6s+b5BzzkWQRsv3b5MmblB87obQRsv3b4k0b9BrwETQRsv3b7mSLpBwFYYQRsv3b5RisBBX8QPQRsv3b6h+bpBcxkVQRsv3b4LO8FBpZMMQRsv3b7Np7tBwH0RQRsv3b74/sFBd04JQRsv3b4hOrxB48QNQRsv3b6LpsJBF/sFQRsv3b7wzLxBMmoKQRsv3b6eOsNB2qYCQRsv3b7lX71B9RUHQRsv3b6TzcNBd+7+QBsv3b567b1BIK8CQRsv3b7ViMRBylL4QBsv3b6NKr5Bsdz7QBsv3b6O6MRB+F/xQBsv3b4iXr5BnIL0QBsv3b5UH8VBAWHqQBsv3b4Xkr5BpYPtQBsv3b5JU8VBCWLjQBsv3b4Mxr5BrYTmQBsv3b4+h8VBEmPcQBsv3b4B+r5BtoXfQBsv3b4zu8VBGWTVQBsv3b72Lb9BxYbYQBsv3b4o78VBIWXOQBsv3b7sYb9BzYfRQBsv3b4eI8ZB6mvHQBsv3b63lb9BvV3KQBsv3b5JWMZBpIXAQBsv3b63wr9BqpPCQBsv3b6KisZB+5G5QBsv3b7V2L9ByOy6QBsv3b55o8ZBPImyQBsv3b5I779BCeSzQBsv3b7sucZBfICrQBsv3b67BcBBSdusQBsv3b5f0MZBvXekQBsv3b4uHMBBitKlQBsv3b7S5sZB/W6dQBsv3b6hMsBBysmeQBsv3b5F/cZBPmaWQBsv3b4UScBBC8GXQBsv3b64E8dBfl2PQBsv3b6HX8BBU7iQQBsv3b4rKsdBvlSIQBsv3b77dcBBkq+JQBsv3b6fQMdB/0uBQBsv3b5ujMBBzKaCQBsv3b4SV8dBgIZ0QBsv3b7hosBBGjx3QBsv3b6FbcdBAnVmQBsv3b5UucBBnCppQBsv3b74g8dBg2NYQBsv3b7Hz8BBHRlbQBsv3b5rmsdBgmhKQBsv3b4b5sBB8F5MQBsv3b7GscdBXm08QBsv3b4W8MBBDok9QBsv3b6GvMdBP1suQBsv3b5f+MBBaFsvQBsv3b7gxMdB5UUgQBsv3b6pAMFB/0UhQBsv3b4rzcdBhzASQBsv3b7zCMFBsDATQBsv3b501cdBKhsEQBsv3b4+EcFBUxsFQBsv3b7A3cdBoAvsPxsv3b6IGcFB1AvuPxsv3b4K5sdB4+DPPxsv3b7SIcFBNuHRPxsv3b5U7sdBK7azPxsv3b4dKsFBfra1Pxsv3b6e9sdBdouXPxsv3b5nMsFBqouZPxsv3b7p/sdBdMF2Pxsv3b6xOsFBGsJ6Pxsv3b4yB8hBBGw+Pxsv3b78QsFBqmxCPxsv3b5+D8hBlxYGPxsv3b5GS8FB/xYKPxsv3b7IF8hBQIKbPhsv3b6QU8FBjIOjPhsv3b4SIMhBgF2rPRsv3b7bW8FBrmLLPRsv3b5cKMhB86YLvhsv3b4lZMFBpkr3vRsv3b6nMMhBaH62vhsv3b5vbMFBHX2uvhsv3b7wOMhBqJQTvxsv3b66dMFBApQPvxsv3b48QchBK7lLvxsv3b7+fMFBZFhJvxsv3b6wSchBKuqBvxsv3b6LfsFB47mBvxsv3b5XS8hBbBCevxsv3b6LfsFBbBCevxsv3b5YS8hBXjy6vxsv3b6LfsFBXjy6vxsv3b5YS8hBTmjWvxsv3b6LfsFBTmjWvxsv3b5YS8hBP5Tyvxsv3b6LfsFBP5Tyvxsv3b5YS8hBGGAHwBsv3b6LfsFBGGAHwBsv3b5YS8hBEHYVwBsv3b6LfsFBEHYVwBsv3b5YS8hBCowjwBsv3b6LfsFBCowjwBsv3b5YS8hBAqIxwBsv3b6LfsFBAqIxwBsv3b5YS8hB+rc/wBsv3b6LfsFB+rc/wBsv3b5YS8hB881NwBsv3b6LfsFB881NwBsv3b5YS8hB6+NbwBsv3b6LfsFB6+NbwBsv3b5YS8hB5PlpwBsv3b6LfsFB5PlpwBsv3b5YS8hB3A94wBsv3b6LfsFB3A94wBsv3b5YS8hB6hKDwBsv3b6LfsFB6hKDwBsv3b5YS8hB5h2KwBsv3b6LfsFB5h2KwBsv3b5YS8hB4iiRwBsv3b6LfsFB4iiRwBsv3b5YS8hB3zOYwBsv3b6LfsFB3zOYwBsv3b5YS8hB2z6fwBsv3b6LfsFB2z6fwBsv3b5YS8hB10mmwBsv3b6LfsFB10mmwBsv3b5YS8hB1FStwBsv3b6LfsFB1FStwBsv3b5YS8hBxl+0wBsv3b6LfsFBG2C0wBsv3b5YS8hBTGi7wBsv3b6AfsFBin27wBsv3b5LS8hB1XDCwBsv3b7Me8FBpprCwBsv3b6RSMhByXvJwBsv3b4XecFBk6XJwBsv3b7cRchBvYbQwBsv3b5jdsFBh7DQwBsv3b4oQ8hBsZHXwBsv3b6uc8FBgrvXwBsv3b5zQMhBpZzewBsv3b75cMFBb8bewBsv3b6+PchBmaflwBsv3b5FbsFBY9HlwBsv3b4KO8hBjbLswBsv3b6Qa8FBV9zswBsv3b5VOMhBgb3zwBsv3b7caMFBS+fzwBsv3b6hNchBdcj6wBsv3b4nZsFBRvL6wBsv3b7sMshBtekAwRsv3b5yY8FBmv4AwRsv3b43MMhBL28EwRsv3b6+YMFBFIQEwRsv3b6DLchBqfQHwRsv3b4JXsFBjgkIwRsv3b7OKshBI3oLwRsv3b5VW8FBCI8LwRsv3b4aKMhBnP8OwRsv3b6gWMFBhRQPwRsv3b5lJchBF4USwRsv3b7rVcFB/JkSwRsv3b6wIshBkQoWwRsv3b43U8FBdh8WwRsv3b78H8hBC5AZwRsv3b6CUMFB8KQZwRsv3b5HHchBeBMdwRsv3b7QTcFBvjkdwRsv3b6CGshBvpYgwRsv3b6fRsFB7M4gwRsv3b4yE8hBDRwkwRsv3b5IP8FBwlQkwRsv3b7aC8hBbaEnwRsv3b7wN8FBJtonwRsv3b6CBMhBzCYrwRsv3b6YMMFBhV8rwRsv3b4q/cdBLKwuwRsv3b5AKcFB4eQuwRsv3b7S9cdBizEywRsv3b7pIcFBQGoywRsv3b577sdB67Y1wRsv3b6RGsFBpO81wRsv3b4j58dBSjw5wRsv3b45E8FB/3Q5wRsv3b7L38dBqsE8wRsv3b7iC8FBX/o8wRsv3b502MdBCUdAwRsv3b6KBMFBwn9AwRsv3b4c0cdBacxDwRsv3b4y/cBBIgVEwRsv3b7EycdByFFHwRsv3b7a9cBBfYpHwRsv3b5swsdBLNRKwRsv3b6L7sBBPyZLwRsv3b7cusdBvlROwRsv3b6y4MBBmMxOwRsv3b53rMdBINhRwRsv3b6az8BBYlxSwRsv3b4lm8dB+FxVwRsv3b56vsBBOuFVwRsv3b4FisdBz+FYwRsv3b5brcBBEWZZwRsv3b7meMdBp2ZcwRsv3b47nMBB6epcwRsv3b7GZ8dBf+tfwRsv3b4ci8BBwW9gwRsv3b6nVsdBVnBjwRsv3b78ecBBmPRjwRsv3b6HRcdBXvNmwRsv3b7maMBBAodnwRsv3b4iNMdBKWhqwRsv3b5GVMBBF39rwRsv3b56G8dBQdptwRsv3b7dIcBBvmRvwRsv3b5048ZB8FlxwRsv3b7K7r9BcORywRsv3b5hsMZBGtx0wRsv3b6Uu79BiVF2wRsv3b5WfsZBRGV4wRsv3b6Ojb9B0pJ5wRsv3b7QU8ZBZu57wRsv3b7cbL9BGul8wRsv3b4kNcZBjXF/wRsv3b5nTL9BIDaAwRsv3b6vFMZBWnqBwRsv3b7yK79Bs/eBwRsv3b469MVBEj2DwRsv3b5pC79BuLCDwRsv3b5d1MVBrQGFwRsv3b7R775BF16FwRsv3b4qusVBjMWGwRsv3b5Q275BbxSHwRsv3b5TpsVB1YeIwRsv3b7jxr5BudaIwRsv3b7mkcVBHUqKwRsv3b51sr5BAZmKwRsv3b54fcVBZgyMwRsv3b4Inr5BSVuMwRsv3b4LacVBm8+NwRsv3b6Rib5BoBaOwRsv3b7rVMVB8pOPwRsv3b4leb5BwcqPwRsv3b4VRcVBhFeRwRsv3b4Wbb5B+4WRwRsv3b5EOcVBGRqTwRsv3b4OYb5BkEiTwRsv3b48LcVBr9yUwRsv3b4GVb5BJguVwRsv3b40IcVBRZ+WwRsv3b7+SL5BvM2WwRsv3b4sFcVB22GYwRsv3b72PL5BUpCYwRsv3b4kCcVBIiWawRsv3b7pML5Bt02awRsv3b49/cRBMOmbwRsv3b7mJ75B0gWcwRsv3b539MRBm6ydwRsv3b4MIr5BLcOdwRsv3b6z7sRBUG+fwRsv3b40HL5B5IWfwRsv3b7b6MRBBjKhwRsv3b5bFr5BmkihwRsv3b4C48RBu/SiwRsv3b6DEL5BTQujwRsv3b4q3cRBcLekwRsv3b6rCr5BAs6kwRsv3b5S18RBJnqmwRsv3b7TBL5BupCmwRsv3b560cRBVi+owRsv3b6Z/71B0riowRsv3b72xsRBZ8mpwRsv3b6kw71BFFmrwRsv3b7wYcRBDmWrwRsv3b4RPL1ByIKtwRsv3b5ussNBaxGtwRsv3b7Mr7xBJS+vwRsv3b4pJsNByb2uwRsv3b6HI7xBg9uwwRsv3b7kmcJBJmqwwRsv3b5Cl7tB4IeywRsv3b6fDcJBCxOywRsv3b4qDLtBUE60wRsv3b6PeMFBmo2zwRsv3b6HhbpBJii3wRsv3b7FScBBVLK0wRsv3b6mZ7lBi2W5wRsv3b6wUb5BEvi1wRsv3b4aMLhBSau6wRsv3b4kGr1B0T23wRsv3b6N+LZBCPG7wRsv3b6X4rtBj4O4wRsv3b4BwbVBxja9wRsv3b4Lq7pB17i5wRsv3b4um7RBA/i+wRsv3b6N7rhBYYq6wRsv3b6KUrNBx6jAwRsv3b4ASrZBAhe7wRsv3b65vrFBNIHBwRsv3b5JALRBj6y7wRsv3b6CFbBBwRbCwRsv3b4SV7JBG0K8wRsv3b5LbK5BTqzCwRsv3b7arbBBp9e8wRsv3b4Uw6xB2kHDwRsv3b6jBK9Bs2i9wRsv3b5aKKtBJfnDwRsv3b7D7qxB+LW9wRsv3b4ilqlBHXvEwRsv3b4vOapBEbW9wRsv3b6S5qdB3oHEwRsv3b6S5qdBEbW9wRsv3b7SI6ZB3oHEwRsv3b7SI6ZBEbW9wRsv3b4TYaRB3oHEwRsv3b4TYaRBEbW9wRsv3b5UnqJB3oHEwRsv3b5UnqJBEbW9wRsv3b6V26BB3oHEwRsv3b6V26BBEbW9wRsv3b7WGJ9B3oHEwRsv3b7WGJ9BerW9wRsv3b4lSZ1BzH7EwRsv3b4vt51B2u+9wRsv3b5ge5tBBqzEwRsv3b5la5xBkDO+wRsv3b5Su5lB0OzEwRsv3b5/v5pB7na+wRsv3b6j/ZdBLjDFwRsv3b7QAZlBTLq+wRsv3b70P5ZBjHPFwRsv3b4hRJdBqv2+wRsv3b5FgpRB6rbFwRsv3b5yhpVBC0G/wRsv3b6CxJJBMPrFwRsv3b5byZNBY4i/wRsv3b6f9JBBrCLGwRsv3b7vlJJBoCDAwRsv3b61OI9BRY7GwRsv3b5gcJFBnLPAwRsv3b6Zjo1BQSHHwRsv3b5Exo9BmUbBwRsv3b5+5ItBPrTHwRsv3b4pHI5BldnBwRsv3b5iOopBOkfIwRsv3b4NcoxBkmzCwRsv3b5HkIhBN9rIwRsv3b7yx4pBRgXDwRsv3b5014ZBTELJwRsv3b40jIlBzeDDwRsv3b56N4VBOczJwRsv3b4lkIhBPszEwRsv3b57sINBY5/KwRsv3b6+ModB1bTFwRsv3b5gLoJB+ofLwRsv3b6jsIVBa53GwRsv3b5GrIBBkHDMwRsv3b6KLoRBAobHwRsv3b5ZVH5BJ1nNwRsv3b5wrIJBnHjIwRsv3b79MXtBn/bNwRsv3b5om4FB6q7JwRsv3b40a3hBg7HOwRsv3b6azoBBeerKwRsv3b5W2nVBoNHPwRsv3b7JRn9BzSLMwRsv3b5XUHNB9AnRwRsv3b7KvHxBIVvNwRsv3b5ZxnBBR0LSwRsv3b7MMnpBdZPOwRsv3b5aPG5Bm3rTwRsv3b7PqHdBeMTPwRsv3b5swmtB1OnUwRsv3b5WpnRBuNDQwRsv3b5NDWlBczfWwRsv3b5vUHFBdd/RwRsv3b7VP2ZB80zXwRsv3b4ncW5B/e7SwRsv3b5DcGNBe1zYwRsv3b6XoWtBhf7TwRsv3b6zoGBBA2zZwRsv3b4H0mhBnQfVwRsv3b4x411B06vawRsv3b4Je2VBv+XVwRsv3b7U+VpBfNDbwRsv3b6TrWFBq7jWwRsv3b6p51dBfrbcwRsv3b4vVl5B1o3XwRsv3b5ZzVRBqYvdwRsv3b7gO1tBwWHYwRsv3b7Dt1FBL2rewRsv3b4p/ldBrCPZwRsv3b7OuE5BNXPfwRsv3b6cyVNB1KHZwRsv3b54e0tBZiLgwRsv3b52dk9BwCXawRsv3b50HUhBUqbgwRsv3b5yGExBY6nawRsv3b5ZwURBayzhwRsv3b4drEhBYyPbwRsv3b6tkEFBzNXhwRsv3b6Z7ENBYzzbwRsv3b7BOj5B5QXiwRsv3b7bED9BG1jbwRsv3b73tjpBnSHiwRsv3b4RjTtBxnPbwRsv3b70NDdBuj3iwRsv3b74+zdBZ4vbwRsv3b5guDNB5VbiwRsv3b49PzRBapbbwRsv3b7vODBBsGLiwRsv3b62jjBBhaHbwRsv3b63syxBy23iwRsv3b5+CS1BoKzbwRsv3b5/LilB5njiwRsv3b5GhClBtLfbwRsv3b65qyVBNITiwRsv3b607CVBZL3bwRsv3b5rKyJBKIriwRsv3b6BQSJBY73bwRsv3b6GqB5BMIriwRsv3b6GqB5BY73bwRsv3b4IIxtBMIriwRsv3b4IIxtBY73bwRsv3b6KnRdBMIriwRsv3b6KnRdBY73bwRsv3b4MGBRBMIriwRsv3b4MGBRBZL3bwRsv3b5klRBBJ4riwRsv3b5MfRBBLbfbwRsv3b6bEw1BwoPiwRsv3b7j3AxBQK/bwRsv3b4GjwlByHviwRsv3b7CUQlBUafbwRsv3b6tCQZB2XPiwRsv3b5lzAVBYp/bwRsv3b5ShAJB6mviwRsv3b4KRwJBc5fbwRsv3b7v/f1A+2PiwRsv3b5gg/1AhY/bwRsv3b738/ZABlziwRsv3b4hc/ZAzobbwRsv3b6+7u9ADlPiwRsv3b78P+9A9XjbwRsv3b5G6ehA70TiwRsv3b7fEuhAFGvbwRsv3b4l3+FADjfiwRsv3b6+COFAM13bwRsv3b4D1dpALSniwRsv3b6c/tlAUk/bwRsv3b7iytNATBviwRsv3b579NJAcUHbwRsv3b7BwMxAaw3iwRsv3b5a6stAkDPbwRsv3b6ftsVAiv/hwRsv3b444MRA0yXbwRsv3b64ur5Am/DhwRsv3b5ma71AvQjbwRsv3b4kz7dA38/hwRsv3b7ZnbVA493awRsv3b7F2LBAs6LhwRsv3b6KPq5AwrLawRsv3b4O1qlAknfhwRsv3b7TO6dAoofawRsv3b5W06JAckzhwRsv3b4jOaBAglzawRsv3b6h0JtAUiHhwRsv3b5mNplAtTHawRsv3b6K2pRAu/PgwRsv3b4E1ZFAK/rZwRsv3b4DCY5ANbDgwRsv3b6rpolAQqPZwRsv3b4bPYdAa07gwRsv3b7G6oFAEkvZwRsv3b73VIBAO/bfwRsv3b5DBXZA4vLYwRsv3b6n2XJAC57fwRsv3b77NGhAQJzYwRsv3b5cQmVANDrfwRsv3b5KuVhADirYwRsv3b6tQlhA4p7ewRsv3b6gL0dAiYrXwRsv3b40b0tAeuHdwRsv3b4VwjdAgefWwRsv3b5dTT5Acj7dwRsv3b5LoCpAN0/WwRsv3b6h5TFA1krcwRsv3b66ChhArF3VwRsv3b4CcSdAi7zawRsv3b7lEgZAIUDUwRsv3b7v0hxAE3rZwRsv3b7oCvQ/lR3TwRsv3b6e0BFAk3fYwRsv3b4EgeA/Tw3SwRsv3b7VYwZAnYHXwRsv3b56zss/1/zQwRsv3b4i5vU/H43WwRsv3b6nV7c/6v/PwRsv3b4iDN4/5LLVwRsv3b4+sqI/TwnPwRsv3b6v7sU/o9zUwRsv3b55z40/CSfOwRsv3b4a9Kw/FSDUwRsv3b725HE/SVHNwRsv3b5iYpM/NHTTwRsv3b6cAkk/tJnMwRsv3b6S4nE/p+LSwRsv3b7uzx4/s/DLwRsv3b5MDTw/hF3SwRsv3b61luk+MWvLwRsv3b5JywQ/nPTRwRsv3b5orZE+i/LKwRsv3b6leJo+qZHRwRsv3b4Ctd09JJnKwRsv3b5e7qI92k3RwRsv3b7AVHu9S1nKwRsv3b6wWBK+4hnRwRsv3b7XQHm+FSzKwRsv3b6JtLy+hfbQwRsv3b5GY9O+ASnKwRsv3b5Bthi/J/XQwRsv3b5nxBK/G0bKwRsv3b5dWFO/FwTRwRsv3b4QBDe/ZqPKwRsv3b7MmYa/SEDRwRsv3b4qf1q/3x/LwRsv3b6vnKK/q5jRwRsv3b7mOIG/47/LwRsv3b5Iyb2/1xTSwRsv3b6QHZa/I3HMwRsv3b4Rc9i/cJ7SwRsv3b499Kq/U0DNwRsv3b4VA/K//VDTwRsv3b5/0MC/HRDOwRsv3b5OzQXAEf/TwRsv3b5ldNa/3wDPwRsv3b6KChLAesjUwRsv3b7awuq/k+/PwRsv3b52/B3Am7fVwRsv3b7QWAHA8djQwRsv3b6s3CnAs7XWwRsv3b7eSw7A3LPRwRsv3b6rCDbAUaHXwRsv3b40YBvAzY/SwRsv3b51QkLAQoXYwRsv3b4dDSjA7WLTwRsv3b57iE7AVmvZwRsv3b6cbjXALCzUwRsv3b4jClvAYD/awRsv3b4WmULAVPXUwRsv3b6PiWfAHhTbwRsv3b5K1E/AmLHVwRsv3b5xK3TAbeHbwRsv3b48mF3As2jWwRsv3b4XfoDAPZ7cwRsv3b4jzmrA0R7XwRsv3b4f3YbAd2LdwRsv3b4YkHjA6MPXwRsv3b5+Vo3AnRbewRsv3b6tVIPAqGfYwRsv3b772ZPAK8PewRsv3b4KM4rAE/zYwRsv3b7DJJrAio3fwRsv3b5EGpPAkEXZwRsv3b6azaDAfvjfwRsv3b6MIZzA/JLZwRsv3b7KvafA6kXgwRsv3b68EaPAaeDZwRsv3b77ra7AVpPgwRsv3b7lAarAeizawRsv3b47erXA+urgwRsv3b6e/7HAL1HawRsv3b5+VrzAexjhwRsv3b5LLbrA+nLawRsv3b7YWMPA3DrhwRsv3b64TcHA15TawRsv3b68XsrAuVzhwRsv3b6cU8jAtLbawRsv3b6fZNHAln7hwRsv3b5/Wc/AfNjawRsv3b4BZtjAEKHhwRsv3b44gdbADvXawRsv3b7GVd/AXMDhwRsv3b76NN7AZv3awRsv3b7hTebA5MnhwRsv3b7QyuXA4gXbwRsv3b6MWO3AYNLhwRsv3b571ezAXg7bwRsv3b42Y/TA3NrhwRsv3b4l4PPA2hbbwRsv3b7hbfvAWOPhwRsv3b7Q6vrAVh/bwRsv3b5GPAHB1OvhwRsv3b69+gDB0ifbwRsv3b6bwQTBUPThwRsv3b4SgATBRTDbwRsv3b7qPwjBEf3hwRsv3b4QOgjBVCnbwRsv3b7JvQvB5PXhwRsv3b6J9wvBWSHbwRsv3b6tQg/B4O3hwRsv3b5VgA/BXhnbwRsv3b4HyBLB5eXhwRsv3b6vBRPBYhHbwRsv3b5hTRbB6d3hwRsv3b4NixbBZgnbwRsv3b670hnB7dXhwRsv3b5nEBrBagHbwRsv3b4VWB3B8c3hwRsv3b69lR3Bb/nawRsv3b5v3SDB9sXhwRsv3b4XGyHBfvHawRsv3b4jXyTBrb3hwRsv3b7OuyTBbuLawRsv3b5mxSfBrqbhwRsv3b7/HSnBIJvawRsv3b4yJSvB8lDhwRsv3b4NWS3BIFLawRsv3b7Jni7B8gfhwRsv3b6k0jDBIAnawRsv3b5hGDLB8r7gwRsv3b48TDTBIMDZwRsv3b74kTXB8nXgwRsv3b7UxTfBIHfZwRsv3b6OCznB8izgwRsv3b5uPzvBHy7ZwRsv3b4mhTzB8ePfwRsv3b4GuT7BxObYwRsv3b4h7T/BnY7fwRsv3b6wtkLBg3zYwRsv3b7ALEPBl/rewRsv3b7lN0fB/OPXwRsv3b61ZkbBVkXewRsv3b70GkvBE0jXwRsv3b6OtEnBbandwRsv3b7NaE7BK6zWwRsv3b5nAk3BhQ3dwRsv3b6mtlHBQhDWwRsv3b5AUFDBnHHcwRsv3b5/BFXBWnTVwRsv3b4ZnlPBtNXbwRsv3b5YUljBbt/UwRsv3b6lylbBYwXbwRsv3b78mVzBpwjUwRsv3b7WpFnB38nZwRsv3b6S42DBJRDTwRsv3b5phVzBYrPYwRsv3b4mIGTBHBTSwRsv3b7OcF/BWbfXwRsv3b6MC2fBExjRwRsv3b4zXGLBULvWwRsv3b7w9mnBChzQwRsv3b6YR2XBRr/VwRsv3b5Z4mzBnCrPwRsv3b5PFmjBrHPUwRsv3b7XpHDBWgfOwRsv3b55b2rBvKzSwRsv3b63XXTB6rbMwRsv3b5TqGzBkRvRwRsv3b7NCXfB3V7LwRsv3b697m7BhMPPwRsv3b43UHnB0AbKwRsv3b4mNXHBdmvOwRsv3b6ilnvBwq7IwRsv3b6Qe3PBaBPNwRsv3b4M3X3BvF3HwRsv3b6ZtnXBpobLwRsv3b5hPIDBpPfFwRsv3b4Pr3fBGs3JwRsv3b5HdYHBJILEwRsv3b6enHnB2UHIwRsv3b6reoLBFArDwRsv3b6djXvByMnGwRsv3b4rc4PBBZLBwRsv3b6bfn3Bu1HFwRsv3b6pa4TBNiHAwRsv3b5zZn/BR6PDwRsv3b59hoXBc6S+wRsv3b4xg4DBZbLBwRsv3b59lobBXBG9wRsv3b7KPIHB2Om/wRsv3b7UaYfB+3e7wRsv3b5r+YHBd1C+wRsv3b52JojBmd65wRsv3b4LtoLBFbe8wRsv3b4W44jBp1a4wRsv3b6Xa4PB8Jq6wRsv3b7T1InB1LS2wRsv3b5u1IPBHlm4wRsv3b67bYrByP+0wRsv3b5PP4TBTp22wRsv3b5I2orB70mzwRsv3b5iqoTBdee0wRsv3b5aRYvBPaexwRsv3b67EYXB+qGywRsv3b5hzIvBKv6vwRsv3b6gJYXBczGwwRsv3b6r8YvBaD+uwRsv3b4cKoXBBFGuwRsv3b7S9ovBr3yswRsv3b6rLoXBSo6swRsv3b5h+4vBcLOqwRsv3b5lM4XBgPyqwRsv3b6q/ovBTuuowRsv3b4KVYXBtWypwRsv3b4GHYzBtimnwRsv3b7NdoXBE6ynwRsv3b63PozBN2SlwRsv3b7nmIXBswimwRsv3b7qXYzBsZyjwRsv3b4zzYXB0oWkwRsv3b5SiozBP9uhwRsv3b4TE4bB1uaiwRsv3b4wy4zBYhqgwRsv3b7vWIbBfkOhwRsv3b4yDI3BSFaewRsv3b5br4bBDtOfwRsv3b4AUo3BepucwRsv3b5lIYfBAk6ewRsv3b4Xt43BuuCawRsv3b6hk4fBk8icwRsv3b6sGo7BQCmZwRsv3b49I4jBjXWbwRsv3b6eiY7BEICXwRsv3b7NyYjBUPuZwRsv3b6NHo/B7MaVwRsv3b4reInBv/iYwRsv3b7neI/BtD+UwRsv3b7ThIrBViCYwRsv3b7iGpDBP8OSwRsv3b6djovBCv+WwRsv3b7i4JDBKmWRwRsv3b5ay4zBRTyWwRsv3b4OkpHBzzKQwRsv3b5DJo7BzE+VwRsv3b7WoZLBE/2OwRsv3b5ni4/BII+UwRsv3b7IcZPBmBeOwRsv3b5YKJHBRwuUwRsv3b47cpTBoDqNwRsv3b55uJLBpkeTwRsv3b7E0pXBeXKMwRsv3b5HX5TBlcGSwRsv3b696JbBgt+LwRsv3b5mFpbBv1KSwRsv3b6SPZjBLFGLwRsv3b7TwJfBLcGRwRsv3b6a8ZnBmL6KwRsv3b6DZ5nBUyWRwRsv3b7WspvB3yKKwRsv3b5BDJvBc4OQwRsv3b52aJ3BUoiJwRsv3b4KrpzBvNiPwRsv3b5BNJ/BCNmIwRsv3b5zRZ7BthGPwRsv3b4hBKHB/yOIwRsv3b4S1p/BeTWOwRsv3b6g56LBR1uHwRsv3b5dP6HBg8OMwRsv3b71XqXBkDeGwRsv3b7nXKLBPrKKwRsv3b6teqfBpuGEwRsv3b63ZaPBmeSIwRsv3b5V46jBGHaDwRsv3b6bX6TB5DaHwRsv3b4/C6rBm/+BwRsv3b4STqXBDZCFwRsv3b6UGKvBDYWAwRsv3b5CL6bB+cmDwRsv3b6tJazBbfd9wRsv3b5H9abBfPWBwRsv3b6NEq3B7th6wRsv3b73sKfBQhyAwRsv3b4h8K3B/Z13wRsv3b60UqjBHpN8wRsv3b65p67Bal90wRsv3b5U9ajB+gh5wRsv3b6kWK/B7hJxwRsv3b5ahKnBim11wRsv3b6I9a/BJsFtwRsv3b6NE6rBa/VxwRsv3b4Wi7DBFGxqwRsv3b4hmarBLWRuwRsv3b4kGrHBzg5nwRsv3b4TGavBbe5qwRsv3b7GnbHBGsVjwRsv3b53lqvB2+VmwRsv3b6YNLLBzGdgwRsv3b5n4qvBd65iwRsv3b6mlrLB2vBcwRsv3b5tKqzBCR9fwRsv3b624LLBRn9ZwRsv3b4RcqzBTmdbwRsv3b6uLbPBEhJWwRsv3b4Qp6zBlWlXwRsv3b5ea7PBAphSwRsv3b6LyazBUKVTwRsv3b4hkbPBOBVPwRsv3b5o7KzBhiJQwRsv3b7+s7PBqpZLwRsv3b4fD63B9H9MwRsv3b4C2LPB3x5IwRsv3b5VKK3B/KJIwRsv3b7h87PBNaFEwRsv3b7lMK3BMOREwRsv3b5f/bPB4htBwRsv3b6ROa3B3V5BwRsv3b4LBrTBj5Y9wRsv3b49Qq3Bitk9wRsv3b63DrTBXhU6wRsv3b7hSq3BLTU6wRsv3b6cF7TBzZg2wRsv3b5zSq3Bem42wRsv3b4fF7TBIhgzwRsv3b74P63B5cYywRsv3b5MDLTB4pIvwRsv3b50Na3BqUEvwRsv3b7IAbTBow0swRsv3b7wKq3BZrwrwRsv3b5E97PBZIgowRsv3b5rIK3BJzcowRsv3b6/7LPB8SclwRsv3b4qF63B650jwRsv3b7I2LPB9eMhwRsv3b71v6zBn+QewRsv3b70YbPBv38ewRsv3b5sVazBNUIbwRsv3b4X8LLBOBQbwRsv3b4H6qvBrtYXwRsv3b6yhLLBVsYXwRsv3b7dgqvByIwTwRsv3b6K+bHBONUUwRsv3b6N56rBRGEOwRsv3b7r47DBvg0SwRsv3b6e9anBwW4KwRsv3b5sl6/BFyMPwRsv3b4I+ajBHIQHwRsv3b7Wmq7B/30MwRsv3b4mGqjBuI8CwRsv3b6Ev6zBn/cKwRsv3b4GwKbBJiv+wBsv3b61DqrBS1MJwRsv3b6HNaXBVqX6wBsv3b4kaKjBWsAEwRsv3b42TaTBVY0RwRsv3b43AaLBX/8DwRsv3b4kcKLBSYARwRsv3b42oaHBW9UDwRsv3b52nqDBnGsRwRsv3b4sUqDB2a0DwRsv3b4m3J7BGkQRwRsv3b7cj57BFowDwRsv3b4uNJ3BhvEQwRsv3b74B5zBko8CwRsv3b6+ppvBEHIPwRsv3b6heZnBRV8BwRsv3b5CA5rBXSgOwRsv3b7hsZfBEA8DwRsv3b6urpfB/JvuwBsv3b5TGpvBwpYAwRsv3b4TOZbBWjrtwBsv3b5e2JrBZgv8wBsv3b548JTBjmXpwBsv3b7E45nBRUD2wBsv3b4urZPB+ZPowBsv3b4MjpnBQwXvwBsv3b4jCpPB3C/mwBsv3b6UeJnBJ0HowBsv3b6TdZLBClDgwBsv3b6E9pjBZf7gwBsv3b7n+pHBo3bdwBsv3b76uJjBFonZwBsv3b4r+ZHBFonZwBsv3b74xZjBGn7SwBsv3b4r+ZHBGn7SwBsv3b74xZjBxprLwBsv3b5p+ZHBr0nKwBsv3b4rxJjB5OLEwBsv3b5yz5HBuUvBwBsv3b4DjZjBug2+wBsv3b5RhZHB64O5wBsv3b63OZjBEX63wBsv3b7UP5HBzbKvwBsv3b6hw5fB1oqxwBsv3b6rlpDBFeelwBsv3b4GvJbB6eGrwBsv3b5T44/BufiawBsv3b7INpXBHSGowBsv3b4hvY7B/beRwBsv3b6nl5LBSQKfwBsv3b6W4o3BD1e5wBsv3b4cLozBiuiewBsv3b4V84vBIhi6wBsv3b4Lu4vBYn2ewBsv3b5VS4rBTmS5wBsv3b5gSonBp/ScwBsv3b7brIjB0yy3wBsv3b6m3YbBLeagwBsv3b5XgIbBrpCHwBsv3b4A+ojBocydwBsv3b4P14TB2naFwBsv3b634IfBUl6awBsv3b4QO4PB2CiDwBsv3b7XxobBxT2WwBsv3b7WvoHBKNd/wBsv3b6koYXBghWSwBsv3b7hQYDBEr95wBsv3b6mg4TB+iuNwBsv3b6P133BbFNywBsv3b79hoPBT0aIwBsv3b7QL3vByK1qwBsv3b6geYLBy6mCwBsv3b6Nw3jBBhZlwBsv3b7q24HBAfR4wBsv3b6C1XbBVjhcwBsv3b7sMIHBw4xswBsv3b5D6XTBJZRTwBsv3b4yf4DB2fVewBsv3b6Se3PBWrBLwBsv3b6xGYDBO2pRwBsv3b7FVHLBFuo/wBsv3b5KNX/BELhDwBsv3b6yK3HB90Q1wBsv3b47SH7BAW81wBsv3b5YaHDB67orwBsv3b4cyn3BlE8nwBsv3b6i4G/Bpz4fwBsv3b7AU33B6E0ZwBsv3b5DWm/BPeMRwBsv3b5b03zBbeoKwBsv3b50527BXSkHwBsv3b7BeHzBvfP4vxsv3b5U2m7BMHv3vxsv3b6cc3zBdMjcvxsv3b4kzm7B50/bvxsv3b5sZ3zBZnTAvxsv3b7qwW7BclbAvxsv3b6EW3zBVGmjvxsv3b4vzW7BEBKrvxsv3b4lXnzBcrmGvxsv3b70Q2/BdcSUvxsv3b5uwHzBq5NVvxsv3b5OuG/BlKlxvxsv3b7JNH3BwAccvxsv3b4PMXDBgllGvxsv3b4uiH3B8e3Gvhsv3b5CI3HBGMchvxsv3b4NK37BRBQ1vhsv3b76QHLBdM/ivhsv3b6AK3/BDYYiPRsv3b7AZnPB1Y2Uvhsv3b6P73/BpGx6Phsv3b53GXXBXmk1vhsv3b4sdIDBXX/ePhsv3b7z/3bBMO6EvBsv3b7LQ4HB17EhPxsv3b7h/HjBCCm8PRsv3b49v4HBdLtKPxsv3b7Ys3vBWDIqPhsv3b4odILB2nJxPxsv3b7zY37BbJORPhsv3b7kfYPBCHCKPxsv3b6stYDBrKWuPhsv3b6WDITB2e2VPxsv3b62a4LB2OvFPhsv3b6PCIXBX9GgPxsv3b5lDoTBxEbvPhsv3b6xlYbB5V6rPxsv3b6Lx4XBX/8EPxsv3b4Vl4fBdnWwPxsv3b4xl4fBxIQKPxsv3b6vwIjB7EW1Pxsv3b5PU4nBriUUPxsv3b7MfIrBcxy6Pxsv3b6wEYvBq2sdPxsv3b73J4zBWWm+Pxsv3b5y3YzBGBMkPxsv3b5Od43B2aWlPxsv3b7Mt47B+DMJQBsv3b6Eh47B3m2mPxsv3b5fepDB+pcJQBsv3b4XSpDB4jWnPxsv3b7yPJLB/PsJQBsv3b6rDJLB/PynPxsv3b7T/JPBaWMKQBsv3b5u45PB0QioPxsv3b42tZXByGIKQBsv3b5P8JXBYRumPxsv3b6lb5fBqFIJQBsv3b4y6JfB9yekPxsv3b5PMZnB9FgIQBsv3b7cqZnBjDSiPxsv3b758prBPl8HQBsv3b6Ga5vBIkGgPxsv3b6jtJzBiWUGQBsv3b4wLZ3BI0mePxsv3b5Re57BAn0FQBsv3b4/yZ7BzrSdPxsv3b7GSaDB0j4FQBsv3b5qLKDBfT2fPxsv3b5yE6LB7/AFQBsv3b6ytaHB4sGgPxsv3b6J1aPBIbMGQBsv3b7Jd6PBR0aiPxsv3b6hl6XBVHUHQBsv3b7hOaXBrMqjPxsv3b64WafBhjcIQBsv3b74+6bBbNO+Pxsv3b5E/KjBdiUlPxsv3b6nqqnBBtXCPxsv3b47rKrBDQcvPxsv3b6KyqvBw+7HPxsv3b63Y6zBY+s5Pxsv3b5noa3B5hLNPxsv3b7lHq7BqTNEPxsv3b6UXK/BCTfSPxsv3b4S2q/B8HtOPxsv3b7CF7HBtFXXPxsv3b5zk7HBPRZZPxsv3b5o4LLBeMbcPxsv3b50P7PBai5nPxsv3b57/LTB6E/lPxsv3b674LTBNMV7Pxsv3b4G/bbBSA7uPxsv3b44jbbB+qCGPxsv3b6DqbjBp8z2Pxsv3b60ObjBWF+PPxsv3b7/VbrBB4v/Pxsv3b4x5rnBuR2YPxsv3b58ArzBRxEEQBsv3b6si7vBdv+hPxsv3b6A473B8zoJQBsv3b6vH73B/vmuPxsv3b574L/BTjkPQBsv3b4Vsb7BBEW8Pxsv3b5Ln8HBVEsVQBsv3b7TR8DBFGnIPxsv3b4KNsPBWV0bQBsv3b6Q3sHBHI3UPxsv3b7HzMTBXm8hQBsv3b5OdcPBIrHgPxsv3b6EY8bBbrYmQBsv3b7E48TBp7n4Pxsv3b5iKMnBX34wQBsv3b6YzcXBCJUQQBsv3b5rT8vBx/s7QBsv3b4KyMbBf1kdQBsv3b6XZszBqJ9HQBsv3b7excfBX/0oQBsv3b5rZM3BiENTQBsv3b6xw8jBSqE0QBsv3b4/Ys7BaedeQBsv3b6EwcnBK0VAQBsv3b4SYM/BOHxqQBsv3b4SvsrBEFpMQBsv3b5oZ9DBXWB1QBsv3b4Eo8vB0MBeQBsv3b4k0tHBVQOBQBsv3b4nFszBw95yQBsv3b4HntLB7saHQBsv3b67k8zBAjOAQBsv3b6cG9PBiIqOQBsv3b5OEc3BnPaGQBsv3b4vmdPBI06VQBsv3b7ijs3BMLqNQBsv3b7CFtTBvRGcQBsv3b52DM7Byn2UQBsv3b5WlNTBVtWiQBsv3b4Kis7BakGbQBsv3b7rEdXB8JipQBsv3b6dB8/BBAWiQBsv3b5+j9XBi1ywQBsv3b4xhc/BmMioQBsv3b4RDdbBBeK2QBsv3b7q/s/BIl6xQBsv3b6Op9bBrpS9QBsv3b6NNNDBsG66QBsv3b6m9dbBeo3EQBsv3b4TZNDB1KvBQBsv3b4UJ9fBUo7LQBsv3b7Tk9DBrKzIQBsv3b7UVtfBKI/SQBsv3b6Tw9DBiq3PQBsv3b6VhtfB/4/ZQBsv3b5S89DBYa7WQBsv3b5UttfB15DgQBsv3b4SI9HBMa/dQBsv3b4T5tfBrpHnQBsv3b7SUtHBCLDkQBsv3b7TFdjBhZLuQBsv3b6SgtHB37DrQBsv3b6TRdjBXJP1QBsv3b5SstHBtrHyQBsv3b5TddjBM5T8QBsv3b4S4tHBlbL5QBsv3b4UpdjBhcoBQRsv3b7REdLBtVkAQRsv3b7T1NjBYjkFQRsv3b7TQNLBz10EQRsv3b4oCtnBMKgIQRsv3b50SdLBToUIQRsv3b4qFtnBlCkMQRsv3b5xSdLBlCkMQRsv3b4+FtnBEq8PQRsv3b5xSdLBEq8PQRsv3b4+FtnBkDQTQRsv3b5xSdLBkDQTQRsv3b4+FtnBDroWQRsv3b5xSdLBDroWQRsv3b4+FtnBjD8aQRsv3b5xSdLBjD8aQRsv3b4+FtnBCsUdQRsv3b5xSdLBCsUdQRsv3b4+FtnBiEohQRsv3b5xSdLBiEohQRsv3b4+FtnBBtAkQRsv3b5xSdLBBtAkQRsv3b4+FtnBhFUoQRsv3b5xSdLBhFUoQRsv3b4+FtnBA9srQRsv3b5xSdLBA9srQRsv3b4+FtnBgWAvQRsv3b5xSdLBgWAvQRsv3b4+FtnB/+UyQRsv3b5xSdLB/+UyQRsv3b4+FtnBfWs2QRsv3b5xSdLBfWs2QRsv3b4+FtnBDPI5QRsv3b5xSdLB++g5QRsv3b49FtnBQnk9QRsv3b7LS9LBbGE9QRsv3b6OGNnBZP9AQRsv3b6fT9LB2uFAQRsv3b5cHNnB2YREQRsv3b5yU9LBU2dEQRsv3b4vINnBTwpIQRsv3b5EV9LByexHQRsv3b4BJNnBxY9LQRsv3b4XW9LBO3JLQRsv3b7UJ9nBOhVPQRsv3b7qXtLBtPdOQRsv3b6nK9nBsJpSQRsv3b68YtLBKn1SQRsv3b55L9nBJyBWQRsv3b6PZtLBnQJWQRsv3b5MM9nBnaVZQRsv3b5iatLBE4hZQRsv3b4fN9nBEStdQRsv3b41btLBiw1dQRsv3b7yOtnBh7BgQRsv3b4HctLBAZNgQRsv3b7EPtnBfDZkQRsv3b7bddLBvRRkQRsv3b6TQtnBpL5nQRsv3b7JetLBtIVnQRsv3b5aR9nBKEZrQRsv3b6jhNLBOvpqQRsv3b4GUdnBb8tuQRsv3b54jtLBfn9uQRsv3b7aWtnBtlByQRsv3b5NmNLByARyQRsv3b6wZNnB/dV1QRsv3b4hotLBD4p1QRsv3b6EbtnBRVt5QRsv3b72q9LBVw95QRsv3b5YeNnBjOB8QRsv3b7KtdLBnpR8QRsv3b4sgtnB6jKAQRsv3b6fv9LB8QyAQRsv3b4BjNnBjfWBQRsv3b50ydLBl8+BQRsv3b7WldnBcrmDQRsv3b5Q09LB0IiDQRsv3b5un9nBWn6FQRsv3b7A4tLB2ziFQRsv3b4qrtnB00GHQRsv3b5v99LBJPKGQRsv3b5pwtnBGQSJQRsv3b4RDNPBarSIQRsv3b4L19nBX8aKQRsv3b6zINPBsHaKQRsv3b6t69nBpYiMQRsv3b5VNdPB9DiMQRsv3b5PANrB7EqOQRsv3b74SdPBO/uNQRsv3b7yFNrBnhCQQRsv3b7KXtPB1qOPQRsv3b4wKNrBedeRQRsv3b7ogtPBSjqRQRsv3b6ZSNrBK5qTQRsv3b7SsNPBr+mSQRsv3b6ndNrBl1qVQRsv3b6F3tPBG6qUQRsv3b5aotrBzQuXQRsv3b48C9TBpNyWQRsv3b5m19rBdKSYQRsv3b7K99PBxp+ZQRsv3b5bstrBMUeaQRsv3b5nkNPB2OSbQRsv3b5XK9rB78SbQRsv3b5APNPBYz+fQRsv3b4SFNnBgKKcQRsv3b6lMdLBTB2iQRsv3b5yONbBKm6dQRsv3b6P9dDBeP6jQRsv3b5/vNLBkFidQRsv3b4uk8/BfRqkQRsv3b7o0M7BWwidQRsv3b6a5s3B1rejQRsv3b51qMzB/3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+/") +"vertex_count": 2196, +"vertex_data": PackedByteArray("zGaeQRsv3b6BEqPBRTmmQRsv3b7dhKrBNkSfQRsv3b6dJKLB8yunQRsv3b5igKnBhCOgQRsv3b64NKHBQQuoQRsv3b5+kKjB0gKhQRsv3b7SRKDBkeqoQRsv3b6WoKfBIOKhQRsv3b7sVJ/B38mpQRsv3b6wsKbBbsGiQRsv3b4GZZ7BK6mqQRsv3b7MwKXBvKCjQRsv3b4hdZ3BeYirQRsv3b7m0KTB9n2kQRsv3b59h5zBFH2sQRsv3b7VyaPBrVSlQRsv3b7qlJvBCnCtQRsv3b6pt6LBECymQRsv3b7PnprBm0yuQRsv3b6mu6HB6AOnQRsv3b4uqJnBcSSvQRsv3b4HxaDBwdunQRsv3b6NsZjBTPyvQRsv3b5kzp/BmbOoQRsv3b7rupfBJNSwQRsv3b7C157BToupQRsv3b5zxJbBb62xQRsv3b56353B71+qQRsv3b6v0JXBTaGyQRsv3b5Tx5zBhCyrQRsv3b7U1ZTB84qzQRsv3b5tqZvBrfurQRsv3b7d15PBHFq0QRsv3b52q5rB18qsQRsv3b7l2ZLBRim1QRsv3b5+rZnBAZqtQRsv3b7t25HBcPi1QRsv3b6Gr5jBKmmuQRsv3b723ZDBmce2QRsv3b6PsZfBNDavQRsv3b6j4o/Bjay3QRsv3b6AmJbBRfuvQRsv3b7r4o7BF5O4QRsv3b60bZXBX7+wQRsv3b4k3o3BFGG5QRsv3b7cW5TBW4SxQRsv3b4z2IzBECa6QRsv3b7rVZPBVkmyQRsv3b5C0ovBC+u6QRsv3b76T5LBUg6zQRsv3b5RzIrBB7C7QRsv3b4JSpHB0dCzQRsv3b68yYnBdY68QRsv3b6lIZDBiom0QRsv3b7TwYjBQ2u9QRsv3b7R5o7BukG1QRsv3b5itIfBLCy+QRsv3b6xzI3BsPq1QRsv3b7SpYbBIuW+QRsv3b4hvozBprO2QRsv3b5Bl4XBGJ6/QRsv3b6Qr4vB42u3QRsv3b7BiYTBcV7AQRsv3b4jlorBeR64QRsv3b6pfoPBcTjBQRsv3b4ST4nBcsa4QRsv3b4wa4LByP7BQRsv3b71CojBHnG5QRsv3b5kU4HBdanCQRsv3b4n84bByhu6QRsv3b6XO4DBIVTDQRsv3b5a24XBWca6QRsv3b72R37B+P/DQRsv3b6lwYTBxmy7QRsv3b5+JHzBctDEQRsv3b5waIPB7AK8QRsv3b767XnBU43FQRsv3b6rBoLBhJy8QRsv3b7sqnfB6ybGQRsv3b4k5YDBGza9QRsv3b7fZ3XBgsDGQRsv3b48h3/Bz869QRsv3b40KHPBNmPHQRsv3b6DIX3BAWC+QRsv3b7773DBTh/IQRsv3b4+PXrBVeK+QRsv3b4JpW7B3MDIQRsv3b65anfBb2e/QRsv3b4LTmzB+EXJQRsv3b6yE3XBiey/QRsv3b4L92nBEsvJQRsv3b6yvHLBrG7AQRsv3b71rWfBjm7KQRsv3b4Z12/Bet3AQRsv3b6ZWmXBawXLQRsv3b52s2zBNEnBQRsv3b6H9GLBDHrLQRsv3b5wG2rBubXBQRsv3b4EimDBkebLQRsv3b7tsGfBKyHCQRsv3b7GJV7BIF7MQRsv3b4fBmXBo4PCQRsv3b7tv1vBF9PMQRsv3b4FLmLBCOLCQRsv3b5wUFnBKzjNQRsv3b4Fk1/BBEHDQRsv3b4W3VbBKJfNQRsv3b6jH13BaZ/DQRsv3b6ubVTBLvzNQRsv3b7Mg1rBtffDQRsv3b4+AVLBK2TOQRsv3b4zqFfBuEjEQRsv3b5xjE/BBr7OQRsv3b6H8FTBhJrEQRsv3b6yEU3B0g/PQRsv3b7IdVLBM+zEQRsv3b7fl0rB1GLPQRsv3b6p8U/BmTvFQRsv3b4eJkjBa7/PQRsv3b46FE3Bon/FQRsv3b4FrkXBcg7QQRsv3b5dOkrBpcTFQRsv3b46LUPBdVPQQRsv3b6SuUfBqAnGQRsv3b5wrEDBeJjQQRsv3b7IOEXB/03GQRsv3b4WMj7BXeTQQRsv3b7zdULBgonGQRsv3b4xtzvB2SnRQRsv3b7gkj/B7sHGQRsv3b5iNDnBFmXRQRsv3b6B8DzBmfrGQRsv3b7FrjbBwZ3RQRsv3b7kajrBHjPHQRsv3b7tKjTBANjRQRsv3b4i0zfBsmjHQRsv3b53rTHBtBXSQRsv3b6c8TTB+pTHQRsv3b5EKy/B00fSQRsv3b4iHzLBx8HHQRsv3b7voSzBoHTSQRsv3b7NlS/BlO7HQRsv3b6ZGCrBbaHSQRsv3b53DC3BCxvIQRsv3b5XlCfBrdHSQRsv3b4eTyrBj0DIQRsv3b7oESXB4vzSQRsv3b60bCfBzmHIQRsv3b5KiSLBMCDTQRsv3b4+vSTBOoPIQRsv3b45/R/BnUHTQRsv3b4tMSLBqKTIQRsv3b4ncR3BCmPTQRsv3b4apR/BvcXIQRsv3b5A7BrB9YfTQRsv3b6QzxzBRt3IQRsv3b6zZhjBIqPTQRsv3b5B7hnBzPPIQRsv3b6/2RXBC7rTQRsv3b5AVhfBWQrJQRsv3b7RSxPBmNDTQRsv3b5KyBTB4iDJQRsv3b4+vhDBRefTQRsv3b6yNhLBuTbJQRsv3b4IOA7Bv//TQRsv3b4HWQ/BvULJQRsv3b5rsAvBoQ3UQRsv3b7pfQzB607JQRsv3b5hIQnBzxnUQRsv3b7o7gnBGlvJQRsv3b5ZkgbB/iXUQRsv3b7gXwfBRWfJQRsv3b7ZAwTBRTLUQRsv3b5ZywTBp3LJQRsv3b6zegHBpz7UQRsv3b6T/wHB4nbJQRsv3b5R4f3AckPUQRsv3b7Zcf7AK3vJQRsv3b5zwvjAu0fUQRsv3b4LU/nAdH/JQRsv3b6Xo/PABEzUQRsv3b4fNPTAuoPJQRsv3b6zh+7AY1DUQRsv3b7S9u7ABYbJQRsv3b4zdenAz1LUQRsv3b4xWenAEoLJQRsv3b65X+TAq07UQRsv3b7w2ePAG37JQRsv3b7YQN/AtErUQRsv3b4Pu97AJHrJQRsv3b74IdrAvUbUQRsv3b4vnNnAM3bJQRsv3b5zCdXAhkLUQRsv3b4jPNTAFG7JQRsv3b7l98/AfTnUQRsv3b51mc7AkmHJQRsv3b5w4MrAVizUQRsv3b5WOMnAAFXJQRsv3b5twsXAxB/UQRsv3b5TGsTAb0jJQRsv3b5rpMDAMxPUQRsv3b5R/L7A/TvJQRsv3b7HkbvAVwXUQRsv3b7FabnACSjJQRsv3b7LgrbAPe/TQRsv3b6Aw7PAhxLJQRsv3b68aLHAWtnTQRsv3b4Akq7A/vzIQRsv3b6XTKzA0sPTQRsv3b7rdanAh+fIQRsv3b6RNKfAka3TQRsv3b6WL6TAhc/IQRsv3b5MJ6LAhZLTQRsv3b7lhJ7A4bDIQRsv3b4EGJ3AZXHTQRsv3b6VBpnABJLIQRsv3b7f/pfAiFLTQRsv3b5w7ZPAJ3PIQRsv3b675ZLAqjPTQRsv3b471I7AmFTIQRsv3b692I3AqBHTQRsv3b58PonADC7IQRsv3b6p0YjAbubSQRsv3b4NlIPAkQXIQRsv3b4nv4PAHr3SQRsv3b5EzXzAAt3HQRsv3b6DVH3Aj5TSQRsv3b54o3LArrTHQRsv3b7GOHPAsWnSQRsv3b6Q6WfA+IfHQRsv3b5gMmnADTfSQRsv3b4dkFzArFXHQRsv3b7uI1/AUgHSQRsv3b78zFHAEiPHQRsv3b5sBVXAuM7RQRsv3b56rkfAlfDGQRsv3b6Y7ErA+JrRQRsv3b61VT3AvrzGQRsv3b7k70DAyF/RQRsv3b4Q+jHAVYDGQRsv3b43+TbA1BzRQRsv3b7o5CbAVkPGQRsv3b4W6SzA1d/QQRsv3b6m1BzAVwbGQRsv3b7u2CLA1qLQQRsv3b6fxBLAWMnFQRsv3b64yBjA3WXQQRsv3b4ztQjAX4zFQRsv3b58uA7A5yjQQRsv3b5CS/2/Zk/FQRsv3b5OqATA7uvPQRsv3b7mKum/bRLFQRsv3b4wMPW/+K7PQRsv3b5CC9W/dtXEQRsv3b6lD+G/ltvOQRsv3b4qtMK/hJjEQRsv3b4W78y/NPfNQRsv3b7oj7C/k1vEQRsv3b6Zzri/zxLNQRsv3b7kap6/oh7EQRsv3b7oraS/cC7MQRsv3b46R4y/uuHDQRsv3b4gjZC/GUrLQRsv3b7oRXS/06TDQRsv3b602Hi/wGXKQRsv3b7I/E+/82fDQRsv3b6fllC/c4HJQRsv3b6vtCu/GyvDQRsv3b5CVCi/K53IQRsv3b7Kawe/Te7CQRsv3b5JEQC/8LjHQRsv3b4MRsa+jLHCQRsv3b4Sma++x9TGQRsv3b7Uc3u+YHTCQRsv3b7jgDy+U/LFQRsv3b5lNN29ND3CQRsv3b7gxC06bx3FQRsv3b5s1Je81ZzCQRsv3b5opzo+qALGQRsv3b4+S2c9dwLDQRsv3b5ErKw+kQTHQRsv3b4BwjQ+u2bDQRsv3b4wt/o+5wrIQRsv3b4+Pps+GsvDQRsv3b75XSQ/ZRHJQRsv3b6BH9w+hy/EQRsv3b7GXks/+RfKQRsv3b5ngQ4/AJTEQRsv3b7SXnI/lx7LQRsv3b4h8i4/gvjEQRsv3b6brow/UyXMQRsv3b4vaU8/6VzFQRsv3b6NK6A/YizNQRsv3b66BnA/6MDFQRsv3b6fqLM/cTPOQRsv3b57Zog/iiTGQRsv3b4wJ8c/UDrPQRsv3b4m1Jg/t4fGQRsv3b4tpto/OUHQQRsv3b4pW6k/LerGQRsv3b6VJO4/JTjRQRsv3b78Z7o/a0vHQRsv3b5D0ABAVp3RQRsv3b4Sr84/HqrHQRsv3b7kggpAsgbSQRsv3b5sSuQ/zgDIQRsv3b5KFBRAiHnSQRsv3b6h3v0/9ETIQRsv3b7Qpx1AYN/SQRsv3b7TPA1AAnjIQRsv3b6JUidAMyzTQRsv3b700RtAVJjIQRsv3b6ZCjFA4V3TQRsv3b6kySpAP6XIQRsv3b6xyTpA7nHTQRsv3b77/DlAIp7IQRsv3b54hkRAnmbTQRsv3b5hWUlAhILIQRsv3b6mNk5AhDrTQRsv3b6lylhA+lHIQRsv3b6F0VdADO3SQRsv3b6nIWhABA3IQRsv3b4DT2FAon/SQRsv3b4UMndALLTHQRsv3b7SpWpA5/PRQRsv3b5j8oJAV0jHQRsv3b5LzXNAukvRQRsv3b5dFopAEcrGQRsv3b4nv3xAO4rQQRsv3b7e8pBA/jrGQRsv3b5cvIJAj7PPQRsv3b5jf5dAWZzFQRsv3b6C+YZAssjOQRsv3b53xp1AduzEQRsv3b4OJItALObNQRsv3b4IK6NAnzXEQRsv3b6eWI9AZCDNQRsv3b73t6dAkXzDQRsv3b6bjZNAxGDMQRsv3b46E6xAdcLCQRsv3b7lwZdA86LLQRsv3b78XLBAwAfCQRsv3b6Y9ZtA8OXKQRsv3b7wnbRAsEzBQRsv3b6rKKBACynKQRsv3b6Q27hATJHAQRsv3b7zWqRA+mvJQRsv3b50F71AmdW/QRsv3b7YjKhAO6/IQRsv3b5YT8FAshy/QRsv3b5ErqxAstTHQRsv3b7iLMZADlm+QRsv3b4fr7BA6uzGQRsv3b4I78pA/JO9QRsv3b5Gs7RAPQ3GQRsv3b4OfM9Amce8QRsv3b5En7hAaCTFQRsv3b6c9dNAFfm7QRsv3b5EjrxAq0bEQRsv3b54LthA0ye7QRsv3b68bsBAqF7DQRsv3b7yetxAglK6QRsv3b5HSsRAmH7CQRsv3b6OiOBAsn25QRsv3b6VI8hAYZnBQRsv3b4YreRAqKS4QRsv3b5r7MtAkq3AQRsv3b5iyuhAjsm3QRsv3b7Suc9ARNC/QRsv3b6VoexAje+2QRsv3b5pgtNAvue+QRsv3b4uqvBA3hG2QRsv3b7PPNdAavq9QRsv3b5tqPRAoTK1QRsv3b7v+9pAMBq9QRsv3b7La/hAHFS0QRsv3b77t95AkTK8QRsv3b7DTvxAqHOzQRsv3b7ZaeJA6US7QRsv3b42HABBYZGyQRsv3b5ZG+ZAk166QRsv3b5z/QFB5a6xQRsv3b63zelAYnq5QRsv3b402gNB1sywQRsv3b7weu1AEo24QRsv3b4kyAVBSuivQRsv3b7CH/FA8J63QRsv3b43rgdB5AKvQRsv3b4SyPRAjLm2QRsv3b5cgglBxB2uQRsv3b5Ib/hAYtG1QRsv3b4pXAtBgTitQRsv3b4vEfxAW+K0QRsv3b7fQA1BXVGsQRsv3b7Xrf9AffSzQRsv3b60HA9BoWmrQRsv3b5zpgFBxAyzQRsv3b427BBBV4KqQRsv3b4YdQNBeSCyQRsv3b7WxBJBA5ypQRsv3b4rPQVBLh2xQRsv3b64xRRBu6+oQRsv3b7K+gZBrRiwQRsv3b6WsRZBTMGnQRsv3b6GvAhBPyqvQRsv3b5QcxhBYNOmQRsv3b5LfQpBjDauQRsv3b7yPhpBhualQRsv3b7/NgxBpimtQRsv3b7NMxxBevOkQRsv3b475Q1BDBysQRsv3b6rER5BEv6jQRsv3b60lw9BpCarQRsv3b4kxB9BswmjQRsv3b5YSBFBmSaqQRsv3b5ViSFBDhWiQRsv3b7H7xJBZw6pQRsv3b7wbSNBkBqhQRsv3b7GjRRB2funQRsv3b4qNCVB7R2gQRsv3b5YLxZBNv+mQRsv3b681SZBciOfQRsv3b5mzRdBheylQRsv3b5YmyhBtSWeQRsv3b7NXRlBOsekQRsv3b50aipB+SKdQRsv3b6Q6hpBTbWjQRsv3b6uDixBCx+cQRsv3b4seRxBl6+iQRsv3b4FoC1BgR6bQRsv3b5fAR5BmIOhQRsv3b5naS9BiRaaQRsv3b4Sdh9BYVGgQRsv3b74GjFB0QqZQRsv3b4v8CBBqkWfQRsv3b4UlTJBnwGYQRsv3b7HZiJBGyCeQRsv3b4zMzRBoPWWQRsv3b6DzSNBoOOcQRsv3b4d2zVB7+OVQRsv3b5aLiVB8L2bQRsv3b75VTdBttGUQRsv3b7pjyZBtp2aQRsv3b54yThB7cCTQRsv3b505idBlFeZQRsv3b4xYjpBNamSQRsv3b4LLClBLhiYQRsv3b7g1jtBPo6RQRsv3b5+dSpBCPqWQRsv3b4HJD1By3eQQRsv3b7ZtytB6qiVQRsv3b6WqD5B2FmPQRsv3b5R4CxBzFKUQRsv3b74DEBB6DaOQRsv3b4dDi5B3S+TQRsv3b7COkFB3hONQRsv3b4DPC9B7g2SQRsv3b6DZ0JB9/CLQRsv3b5xajBBMeyQQRsv3b66lENBIs6KQRsv3b7SmDFBt8mPQRsv3b6+wkRBPquJQRsv3b5yxzJBUqiOQRsv3b7Q70VBhoiIQRsv3b6e9jNBzIaNQRsv3b68HUdBxmWHQRsv3b7SJTVBpWWMQRsv3b5ES0hBLkOGQRsv3b7TVTZBD0WLQRsv3b4seUlBmCCFQRsv3b4QhjdB/CSKQRsv3b7IpkpBOv6DQRsv3b5atzhBnQWJQRsv3b7s1EtBy9uCQRsv3b4/6TlBKeiHQRsv3b6PAU1BhLmBQRsv3b53HTtB5s6GQRsv3b40LE5BEZeAQRsv3b7iVDxBaL6FQRsv3b5LUE9B5N9+QRsv3b7FmT1BvfCEQRsv3b6uL1BBiZ18QRsv3b6VCT9BH16EQRsv3b6r6lBBJXJ6QRsv3b5FmkBBys6DQRsv3b4uuVFB7l54QRsv3b6UTUJBwU2DQRsv3b4hjFJB1Wh2QRsv3b77I0RBytiCQRsv3b5EZ1NBo5J0QRsv3b5NG0ZB+W2CQRsv3b6RS1RBG+ByQRsv3b6LMkhBiw2CQRsv3b4qOVVBqlNxQRsv3b58ZkpBU7WBQRsv3b4nNFZBU+9vQRsv3b40tExBKGWBQRsv3b66PVdBRbRuQRsv3b6+F09BqRuBQRsv3b6kW1hB16FtQRsv3b53jVFBZNmAQRsv3b4CjFlBhbhsQRsv3b75EVRBe56AQRsv3b6n0lpBlPVrQRsv3b6colZBQGyAQRsv3b4yJFxBll1rQRsv3b77M1lB2zmAQRsv3b6g4l1BvNNqQRsv3b5PultBqfZ/QRsv3b7FLWBB6E1qQRsv3b74PV5BSHV/QRsv3b4WnGJB/MlpQRsv3b5nwWBBYfR+QRsv3b7PEGVB4EdpQRsv3b6zRGNBI3R+QRsv3b7kimdBTcZoQRsv3b7Mx2VBvvN9QRsv3b4fCGpBp0VoQRsv3b4IS2hBLHR9QRsv3b74hWxBY8VnQRsv3b46zmpBuvR8QRsv3b4MBW9BwEVnQRsv3b52UW1BunV8QRsv3b4ZhXFBQsZmQRsv3b671G9B3/Z7QRsv3b4kBXRBZkdmQRsv3b4RWHJBiXh7QRsv3b7ThXZBl8hlQRsv3b4s23RBuvl6QRsv3b7uCHlBx0llQRsv3b5FXndB6Hp6QRsv3b4PjHtB9spkQRsv3b5g4XlBF/x5QRsv3b4uD35BJUxkQRsv3b56ZHxBRX15QRsv3b4kSYBBVM1jQRsv3b6U535BdP54QRsv3b6zioFBgk5jQRsv3b5XtYBBoX94QRsv3b5CzIJBsc9iQRsv3b7j9oFBzAB4QRsv3b7WDYRB21BiQRsv3b5vOINB8YF3QRsv3b5vT4VBRtRhQRsv3b6RdIRB7Ot2QRsv3b5zyIZBBVxhQRsv3b7SfYVBjPR0QRsv3b7kCIpB7QpgQRsv3b7DXYZBR4NxQRsv3b6xt4xBcYleQRsv3b7UZodBrQFwQRsv3b7WwI1B8gddQRsv3b7mb4hBJYBuQRsv3b7uyY5BcIZbQRsv3b74eIlBn/5sQRsv3b4C049B7gRaQRsv3b4JgopBGn1rQRsv3b4W3JBBbINYQRsv3b4ai4tBl/tpQRsv3b4o5ZFB6gFXQRsv3b4slIxBEnpoQRsv3b487pJBZ4BVQRsv3b49nY1Bj/hmQRsv3b5N95NB5P5TQRsv3b5Ppo5BCndlQRsv3b5gAJVBYH1SQRsv3b5gr49Bg/VjQRsv3b5zCZZB3ftQQRsv3b5xuJBBAHRiQRsv3b6EEpdBWXpPQRsv3b6CwZFBfPJgQRsv3b6VG5hB1vhNQRsv3b6TypJB+XBfQRsv3b6mJJlBb3xMQRsv3b4w0JNBC7tdQRsv3b4ZUZpBKfJKQRsv3b48zJRBqMdbQRsv3b6hkJtByFRJQRsv3b5QxpVBOftZQRsv3b6Rp5xBM7RHQRsv3b5bwpZBkE9YQRsv3b5Kqp1BNBhGQRsv3b6SuJdBE1JWQRsv3b6H2Z5BvWNEQRsv3b4hpZhBz0RUQRsv3b5o959BYadCQRsv3b4RlplBdYhSQRsv3b5X6KBBfu5AQRsv3b4lhZpBh6hQQRsv3b5f7KFBgzA/QRsv3b4lbZtB+o5OQRsv3b7aA6NBXmI9QRsv3b7fUJxBC4xMQRsv3b7zAaRBiI87QRsv3b7xNp1BNblKQRsv3b4F6KRBfcA5QRsv3b4qG55Bhr9IQRsv3b4H4aVBeus3QRsv3b6M+J5B7ZVGQRsv3b5d5qZBawk2QRsv3b7k0p9B8olEQRsv3b7k06dBoyM0QRsv3b7vrqBBJ6RCQRsv3b7wr6hBKkAyQRsv3b7viaFBrqZAQRsv3b6llqlB0FswQRsv3b7VX6JB2nQ+QRsv3b6MjqpBk2ouQRsv3b60MKNBvU88QRsv3b59datBuHQsQRsv3b6KA6RB4lk6QRsv3b5TSKxB3n4qQRsv3b5g1qRBCGQ4QRsv3b4pG61BvI4oQRsv3b7YpqVBhjM2QRsv3b5CBq5BwpImQRsv3b5yb6ZB9e8zQRsv3b6J665BxI8kQRsv3b6eOadBjOYxQRsv3b46uK9BM4wiQRsv3b4EBKhBAeMvQRsv3b6egrBB6IkgQRsv3b7rzahBbdItQRsv3b4cUrFBhIkeQRsv3b5ilKlBCJErQRsv3b6KMbJBun4cQRsv3b5bVapBJFMpQRsv3b6aBbNBZW8aQRsv3b4HGKtBz0MnQRsv3b5GyLNBEWAYQRsv3b6y2qtBdTQlQRsv3b7zirRBIVIWQRsv3b7anKxB1BYjQRsv3b7gUrVBZkUUQRsv3b66W61Bfs4gQRsv3b5LJ7ZB0y8SQRsv3b7uFa5BwoweQRsv3b4S8bZBUxYQQRsv3b6F0a5BQnMcQRsv3b6prLdB0/wNQRsv3b4bja9BwlkaQRsv3b4/aLhBVOMLQRsv3b6ySLBBOUAYQRsv3b7ZI7lB6skJQRsv3b4+BLFBwiUWQRsv3b7D37lBVLAHQRsv3b6uv7FBMAsUQRsv3b6Lm7pBqJYFQRsv3b4le7JBhPERQRsv3b4CV7tB/HwDQRsv3b6cNrNB2dcPQRsv3b54ErxBX2MBQRsv3b4O8rNBor0NQRsv3b4gzrxBcZP+QBsv3b5orbRBmaILQRsv3b72ib1ByV/6QBsv3b6vaLVB/YcJQRsv3b6CRb5B/Cv2QBsv3b78I7ZBHG4HQRsv3b7NAL9BWvjxQBsv3b5C37ZBX1MFQRsv3b5mvL9BlsTtQBsv3b5kmrdBlTcDQRsv3b4yeMBBVpDpQBsv3b5xVbhBfxwBQRsv3b6UM8FB/lvlQBsv3b6BELlByQP+QBsv3b7L7sFB7SfhQBsv3b5yy7lBFMr5QBsv3b62qsJBAfPcQBsv3b4dhrpBapD1QBsv3b40ZsNBBr7YQBsv3b7MQLtBVVfxQBsv3b6ZIcRBzIjUQBsv3b4p+7tBJBntQBsv3b6C3cRBA1PQQBsv3b4ytbxBv9foQBsv3b6NmcVBmhzMQBsv3b7Abr1BSpDkQBsv3b4JVsZBsu3HQBsv3b4lJr5BNOnfQBsv3b7FIcdBQvPDQBsv3b6xzL5BOWrYQBsv3b6VT8hBSdm/QBsv3b70Pr9Bx7DNQBsv3b7+eclBsYq7QBsv3b4ig79B2PjBQBsv3b4iMcpByvG2QBsv3b4Mk79Br7W3QBsv3b5qX8pBvfmxQBsv3b4DjL9BKQuxQBsv3b4rWMpBxdysQBsv3b7whL9BFNurQBsv3b78UMpB2r+nQBsv3b7CfL9BpaumQBsv3b6ySMpBSaGiQBsv3b6TdL9BFI2hQBsv3b6DQMpBuoKdQBsv3b5jbL9Bc26cQBsv3b5TOMpBamSYQBsv3b4zZL9BTk2XQBsv3b4fMMpBdUaTQBsv3b7ZW79BsSiSQBsv3b66J8pBRiiOQBsv3b5FU79BrwaNQBsv3b4fH8pBwAmJQBsv3b6wSr9BKeiHQBsv3b6KFspBOuuDQBsv3b4bQr9Bo8mCQBsv3b71DcpBYpl9QBsv3b6GOb9BV1Z7QBsv3b5gBcpBVlxzQBsv3b7yML9BSxlxQBsv3b7M/MlBsB9pQBsv3b5dKL9BKthmQBsv3b409MlBQ+NeQBsv3b6oH79BBJVcQBsv3b5568lBd6ZUQBsv3b7gFr9B2VVSQBsv3b6v4slBdWlKQBsv3b4YDr9B1xhIQBsv3b7n2clBdyxAQBsv3b5QBb9Bt9s9QBsv3b4f0clBde81QBsv3b6H/L5BtZ4zQBsv3b5WyMlBcbIrQBsv3b6/875B02EpQBsv3b6Ov8lBb3UhQBsv3b736r5B0SQfQBsv3b7GtslBbTgXQBsv3b4v4r5Bz+cUQBsv3b7+rclBp/sMQBsv3b5n2b5BZqgKQBsv3b40pclBB78CQBsv3b6M0L5BsGcAQBsv3b5UnMlBYQTxPxsv3b6lx75BiFLsPxsv3b5sk8lBa4rcPxsv3b69vr5BktjXPxsv3b6EislBdRDIPxsv3b7Wtb5BnF7DPxsv3b6dgclBf5azPxsv3b7urL5BpuSuPxsv3b61eMlBiRyfPxsv3b4HpL5BsGqaPxsv3b7Ob8lBlKKKPxsv3b4fm75Bu/CFPxsv3b7mZslBL1FsPxsv3b44kr5BBe5iPxsv3b7/XclBT11DPxsv3b5Rib5Bnvk5Pxsv3b4YVclBY2kaPxsv3b5pgL5BsQURPxsv3b4wTMlBs+viPhsv3b6Cd75B4BvQPhsv3b5IQ8lB3wWRPhsv3b6Sbr5Bzj98Phsv3b5WOslBjH38PRsv3b6WZb5B166wPRsv3b5ZMclBe0IWvRsv3b6ZXL5B8++WvRsv3b5cKMlBN2BJvhsv3b6dU75BdkVvvhsv3b5gH8lBtZe2vhsv3b6hSr5BY4vJvhsv3b5kFslBtT8Evxsv3b6kQb5Bi7kNvxsv3b5nDclBmzMtvxsv3b6oOL5B66w2vxsv3b5rBMlBcydWvxsv3b6sL75Bw6Bfvxsv3b5v+8hBQRt/vxsv3b6wJr5BjEqEvxsv3b5z8shBjQeUvxsv3b6zHb5BecSYvxsv3b526chBf4Govxsv3b63FL5BJz6tvxsv3b564MhBZ/u8vxsv3b67C75BU7jBvxsv3b5+18hBU3XRvxsv3b6+Ar5BPzLWvxsv3b6BzshB7e7lvxsv3b7C+b1Biq/qvxsv3b6DxchBeGj6vxsv3b638L1BCS3/vxsv3b53vMhBMHEHwBsv3b6s571BeNMJwBsv3b5ss8hBIK4RwBsv3b6h3r1BihAUwBsv3b5hqshBE+sbwBsv3b6V1b1BfU0ewBsv3b5VochBCSgmwBsv3b6KzL1BUYoowBsv3b5KmMhB+WQwwBsv3b5/w71BY8cywBsv3b4/j8hB7KE6wBsv3b5zur1BVgQ9wBsv3b4zhshB495EwBsv3b5osb1BK0FHwBsv3b4ofchB0htPwBsv3b5dqL1BPH5RwBsv3b4ddMhBxVhZwBsv3b5Rn71BL7tbwBsv3b4Ra8hBuJVjwBsv3b5Glr1BIvhlwBsv3b4GYshBrNJtwBsv3b46jb1BFjVwwBsv3b76WMhBog94wBsv3b4vhL1B6nF6wBsv3b7vT8hBSSaBwBsv3b4ke71BfleCwBsv3b7kRshBvUSGwBsv3b4Ycr1BJXaHwBsv3b7XPchBJ2OLwBsv3b4Lab1BSJWMwBsv3b7INMhBkYGQwBsv3b70X71BSrSRwBsv3b6wK8hBC6CVwBsv3b7dVr1Bs9KWwBsv3b6ZIshBg76awBsv3b7HTb1BK/GbwBsv3b6DGchB/NyfwBsv3b6wRL1BpA+hwBsv3b5sEMhBdPukwBsv3b6aO71BHC6mwBsv3b5WB8hB6hmqwBsv3b6DMr1Bo0yrwBsv3b4//sdBZDivwBsv3b5sKb1BDGuwwBsv3b4o9cdB3Va0wBsv3b5WIL1BhYm1wBsv3b4S7MdBVXW5wBsv3b4/F71B/ae6wBsv3b774sdBzZO+wBsv3b4pDr1Bdca/wBsv3b7l2cdBRLLDwBsv3b4SBb1B/eTEwBsv3b7O0MdBvtDIwBsv3b77+7xBZgPKwBsv3b63x8dBNu/NwBsv3b7l8rxB3iHPwBsv3b6hvsdBrA3TwBsv3b7O6bxBZUDUwBsv3b6KtcdBJyzYwBsv3b634LxBz17ZwBsv3b5zrMdBn0rdwBsv3b6h17xBR33ewBsv3b5do8dBDWniwBsv3b6KzrxBLJzjwBsv3b5GmsdBbIfnwBsv3b5txbxBmbvowBsv3b4nkcdB1qXswBsv3b5GvLxBmtrtwBsv3b7/h8dBTMTxwBsv3b4gs7xBEPnywBsv3b7ZfsdBweL2wBsv3b75qbxBlhf4wBsv3b6ydcdBOQH8wBsv3b7SoLxB/TX9wBsv3b6LbMdB2I8AwRsv3b6sl7xBOioBwRsv3b5lY8dBEx8DwRsv3b6FjrxBdbkDwRsv3b4+WsdBTq4FwRsv3b5fhbxBsEgGwRsv3b4YUcdBij0IwRsv3b44fLxB7NcIwRsv3b7xR8dBxcwKwRsv3b4Sc7xBJ2cLwRsv3b7LPsdBAFwNwRsv3b7rabxBYvYNwRsv3b6kNcdBO+sPwRsv3b7FYLxBnYUQwRsv3b5+LMdBdnoSwRsv3b6eV7xB4RQTwRsv3b5XI8dBsgkVwRsv3b53TrxBFKQVwRsv3b4wGsdB7ZgXwRsv3b5RRbxBTzMYwRsv3b4KEcdBHSgawRsv3b4qPLxBBsMawRsv3b7hB8dBR7ccwRsv3b70MrxB4VIdwRsv3b6p/sZBfUYfwRsv3b64KbxBSeIfwRsv3b5s9cZBt9UhwRsv3b58ILxBg3EiwRsv3b4w7MZB8WQkwRsv3b5AF7xBtgAlwRsv3b704sZBK/QmwRsv3b4FDrxB8I8nwRsv3b652cZBZYMpwRsv3b7JBLxBMR8qwRsv3b590MZBnxIswRsv3b6N+7tBZK4swRsv3b5Bx8ZB2aEuwRsv3b5S8rtBnj0vwRsv3b4GvsZBEzExwRsv3b4W6btB38wxwRsv3b7KtMZBTcAzwRsv3b7a37tBGVw0wRsv3b6Oq8ZBh082wRsv3b6e1rtBTOs2wRsv3b5SosZBwt44wRsv3b5jzbtBh3o5wRsv3b4XmcZB9W07wRsv3b4oxLtBBQo8wRsv3b7aj8ZBGf09wRsv3b7kurtBHpo+wRsv3b6ThsZBQoxAwRsv3b6LsbtB+ClBwRsv3b44fcZBehtDwRsv3b4yqLtBMLlDwRsv3b7fc8ZBs6pFwRsv3b7ZnrtBaUhGwRsv3b6GasZB6zlIwRsv3b6AlbtBoddIwRsv3b4tYcZBI8lKwRsv3b4njLtB2WZLwRsv3b7UV8ZBXFhNwRsv3b7OgrtBEvZNwRsv3b57TsZBlOdPwRsv3b51ebtBSoVQwRsv3b4iRcZBzXZSwRsv3b4ccLtBgxRTwRsv3b7JO8ZBBQZVwRsv3b7DZrtBu6NVwRsv3b5wMsZBPZVXwRsv3b5qXbtB8zJYwRsv3b4XKcZBVSRawRsv3b4SVLtBfsNawRsv3b65H8ZBarNcwRsv3b6NSrtBB1RdwRsv3b4vFsZBoEJfwRsv3b4IQbtBNONfwRsv3b6qDMZB1tFhwRsv3b6EN7tBanJiwRsv3b4mA8ZBDGFkwRsv3b7/LbtBoAFlwRsv3b6h+cVBQvBmwRsv3b57JLtB1pBnwRsv3b4d8MVBeH9pwRsv3b72GrtBDCBqwRsv3b6Y5sVBrg5swRsv3b5yEbtBQq9swRsv3b4U3cVB5J1uwRsv3b7tB7tBeD5vwRsv3b6P08VB6CxxwRsv3b5q/rpBqc9xwRsv3b4EysVB5btzwRsv3b6j9LpB/WB0wRsv3b41wMVBEkt2wRsv3b7Z6rpBVPB2wRsv3b5ptsVBRNp4wRsv3b4N4bpBjn95wRsv3b6drMVBdml7wRsv3b5B17pBwA58wRsv3b7RosVBp/h9wRsv3b51zbpB8Z1+wRsv3b4FmcVB60OAwRsv3b6pw7pBnZaAwRsv3b45j8VBUIuBwRsv3b7bubpBSOCBwRsv3b5ahcVBstKCwRsv3b6Hr7pB3ymDwRsv3b70esVBSBqEwRsv3b4ypbpBcHGEwRsv3b6fcMVB3WGFwRsv3b7dmrpBBrmFwRsv3b5KZsVBQamGwRsv3b6JkLpBfgKHwRsv3b7lW8VBRPCHwRsv3b67hbpBw0+IwRsv3b7iUMVBbTeJwRsv3b7tebpBG5uJwRsv3b7uRMVBKHaKwRsv3b6WbrpBxTyLwRsv3b4+NMVB4qmLwRsv3b5STbpBBjqNwRsv3b4D/cRBTdqMwRsv3b4NFbpBvjKPwRsv3b7dn8RBEQaOwRsv3b7ixrlBGiGRwRsv3b7oHsRB1yqPwRsv3b4wY7lBewmTwRsv3b5reMNBtEaQwRsv3b6d6bhBSuiUwRsv3b5Rq8JBcliRwRsv3b6AWrhBIbWWwRsv3b54usFBvV6SwRsv3b4Dt7dB1WyYwRsv3b5nqMBBa1iTwRsv3b7N/7ZBvgmawRsv3b6+eb9BPEWUwRsv3b7ONrZB7IibwRsv3b6yNL5BuSKVwRsv3b5eX7VBg/ycwRsv3b4EyrxBQ/KVwRsv3b75b7RBWx6ewRsv3b6If7tBVMeWwRsv3b4weLNBYvqewRsv3b6of7pB/5uXwRsv3b5bf7JBEtKfwRsv3b5Ng7lBnHCYwRsv3b5rhrFBY6mgwRsv3b4vh7hBlESZwRsv3b7hjLBBvX+hwRsv3b7airdBxRiawRsv3b4Yk69B61OiwRsv3b4TkbZB4+yawRsv3b5kma5B0SijwRsv3b50lrVBusCbwRsv3b6Sn61BGP6jwRsv3b7xmrRBdJScwRsv3b6EpaxBetKkwRsv3b4coLNBPWidwRsv3b5kq6tBQ6alwRsv3b78pbJBBjyewRsv3b5EsapBDnqmwRsv3b7aq7FBvg+fwRsv3b44t6lBiU6nwRsv3b7nsLBBO+OfwRsv3b4CvahB6yKowRsv3b6jta9BwragwRsv3b6uwqdBkvaowRsv3b4pu65BS4qhwRsv3b5YyKZBGcqpwRsv3b7VwK1B1F2iwRsv3b4CzqVBpJ2qwRsv3b59xqxBWzGjwRsv3b6t06RBPXGrwRsv3b4TzKtB0QSkwRsv3b5j2aNBTEWswRsv3b4U0apBI9ikwRsv3b733qJBKBmtwRsv3b4D1qlBgKulwRsv3b585KFBh+ytwRsv3b6G26hB3X6mwRsv3b4B6qBB4r+uwRsv3b4N4adBO1KnwRsv3b6G759BQJOvwRsv3b6S5qZBmCWowRsv3b4L9Z5Bn2awwRsv3b4V7KVB8/iowRsv3b6S+p1BFjqxwRsv3b588aRBP8ypwRsv3b4dAJ1B0Q2ywRsv3b6C9qNBdZ+qwRsv3b6QBZxBXOGywRsv3b6R+6JBs3KrwRsv3b76CptBmrSzwRsv3b77AKJB8UWswRsv3b5kEJpB2Ie0wRsv3b5kBqFBLhmtwRsv3b7PFZlBFVu1wRsv3b7PC6BBbOytwRsv3b45G5hBUy62wRsv3b46EZ9Bqr+uwRsv3b6jIJdBkQG3wRsv3b6kFp5B5pKvwRsv3b4PJpZB6NS3wRsv3b7wG51BFGawwRsv3b58K5VBa6i4wRsv3b74IJxBNTmxwRsv3b7YMJRBxnu5wRsv3b4QJptBWwyywRsv3b4vNpNB7U66wRsv3b5kK5pBgN+ywRsv3b6FO5JBEiK7wRsv3b66MJlBprKzwRsv3b7aQJFBN/W7wRsv3b4SNphBzIW0wRsv3b4wRpBBXci8wRsv3b5oO5dB8li1wRsv3b6HS49BhJu9wRsv3b68QJZBFyy2wRsv3b7dUI5BqW6+wRsv3b4SRpVBOv+2wRsv3b41Vo1B6EG/wRsv3b5LS5RBT9K3wRsv3b6QW4xBZxXAwRsv3b4nUJNBUKW4wRsv3b7VYItBtejAwRsv3b4QVZJBWHi5wRsv3b4SZopBvbvBwRsv3b5NWpFBYEu6wRsv3b5Pa4lBxY7CwRsv3b6KX5BBaB67wRsv3b6McIhBzmHDwRsv3b7FZI9BcPG7wRsv3b7IdYdB2DTEwRsv3b7/aY5Bd8S8wRsv3b4Fe4ZB3gfFwRsv3b4+b41Be5e9wRsv3b5GgIVBDNvFwRsv3b5OdIxBa2q+wRsv3b6HhYRBiK7GwRsv3b7neItBQj2/wRsv3b6uioNBwoHHwRsv3b6YfYpBIhDAwRsv3b7Jj4JBoFTIwRsv3b62golBA+PAwRsv3b7llIFBgSfJwRsv3b7Sh4hB47XBwRsv3b4AmoBBY/rJwRsv3b7qjIdBw4jCwRsv3b43Pn9BQs3KwRsv3b4HkoZBm1vDwRsv3b6BSH1BdaDLwRsv3b7BloVBUS7EwRsv3b62UntB63PMwRsv3b72moRB8gDFwRsv3b6nXHlB8kbNwRsv3b51n4NBnNPFwRsv3b6DZndBnhnOwRsv3b5hpIJBRabGwRsv3b5ecHVBR+zOwRsv3b5OqYFB73jHwRsv3b46enNB777PwRsv3b4+roBBhkvIwRsv3b5AhHFBVJLQwRsv3b6ZZH9B3R3JwRsv3b73jW9ByGXRwRsv3b6na31BL/DJwRsv3b5dl21BajjSwRsv3b5OdHtBiMLKwRsv3b6yoGtBwQrTwRsv3b6mfXlB35TLwRsv3b4MqmlBON3TwRsv3b61hndBBmfMwRsv3b60s2dBLLHUwRsv3b4QjHVBtzjNwRsv3b6KvGVBh4TVwRsv3b7nkHNBjgrOwRsv3b4FxWNBXlbWwRsv3b5imXFBPtzOwRsv3b7fzWFByynXwRsv3b4Onm9BSa3PwRsv3b5A1l9BN/7XwRsv3b5Lnm1B+H3QwRsv3b6X3V1BDNHYwRsv3b50oGtB7E3RwRsv3b4g5ltBxanZwRsv3b6kk2lBURXSwRsv3b6k+llBQ7/awRsv3b4R4GZBLr/SwRsv3b66CVhBcQzcwRsv3b6CA2NB5UTTwRsv3b5q91VBJi7dwRsv3b4yjF5BMavTwRsv3b5dxlNB8QPewRsv3b6a91lBkO/TwRsv3b70g1FB1pXewRsv3b7ZG1VBCA7UwRsv3b5dLU9B6tXewRsv3b4Zd1BBKBPUwRsv3b56tUxBw9/ewRsv3b7K9kxBWBXUwRsv3b6UKEpBFeLewRsv3b7tTEpBcxfUwRsv3b44mkdBOeTewRsv3b4LskdBKhjUwRsv3b7IC0VB9eTewRsv3b7PF0VB4BjUwRsv3b5efEJBq+XewRsv3b6jh0JBfxnUwRsv3b557T9BTObewRsv3b4N8j9BahnUwRsv3b6EXj1BN+bewRsv3b4qXT1BVhnUwRsv3b4GzzpBI+bewRsv3b61zTpBQhnUwRsv3b6iPzhBD+bewRsv3b4yPThBDBnUwRsv3b6GsDVB2eXewRsv3b7mqTVBehjUwRsv3b5OITNBReXewRsv3b6eFzNB5xfUwRsv3b7SkTBBsuTewRsv3b4iiDBBVBfUwRsv3b5WAi5BH+TewRsv3b6m+C1BwRbUwRsv3b78citBjOPewRsv3b7IZytBABbUwRsv3b7A4yhByuLewRsv3b631ShBFxXUwRsv3b5iVCZB4eHewRsv3b4HRSZBLhTUwRsv3b7mxCNB+ODewRsv3b6LtSNBRRPUwRsv3b5qNSFBD+DewRsv3b4PJiFBXBLUwRsv3b7upR5BJt/ewRsv3b6Tlh5BcxHUwRsv3b6cFhxBPN7ewRsv3b5pBRxBUhDUwRsv3b5PhxlBGt3ewRsv3b4QdBlBKw/UwRsv3b7Z9xZB89vewRsv3b5g5BZBAw7UwRsv3b5eaBRBy9rewRsv3b7cVBRB2wzUwRsv3b7i2BFBo9newRsv3b5gxRFBswvUwRsv3b5nSQ9Be9jewRsv3b7lNQ9BiwrUwRsv3b74uQxBU9fewRsv3b7mpQxBUgnUwRsv3b6fKgpBGdbewRsv3b4BFQpB+wfUwRsv3b46mwdBwtTewRsv3b6fhAdBpAbUwRsv3b6/CwVBa9PewRsv3b4k9QRBTQXUwRsv3b5EfAJBFNLewRsv3b6pZQJB9gPUwRsv3b6S2f9AvdDewRsv3b5crP9AnwLUwRsv3b6buvpAZs/ewRsv3b5ljfpASAHUwRsv3b6lm/VAD87ewRsv3b5vbvVA9v/TwRsv3b5RhfBAiMzewRsv3b709u9A5/jTwRsv3b7ob+tABMXewRsv3b6NeepAZ/HTwRsv3b7hUeZAer3ewRsv3b69VOVA5+nTwRsv3b4/M+FA+rXewRsv3b4bNuBAZuLTwRsv3b6eFNxAea7ewRsv3b56F9tA5trTwRsv3b789dZA+abewRsv3b7Y+NVAZdPTwRsv3b5d19FAeJ/ewRsv3b4o2tBA8svTwRsv3b7cwMxAZ5fewRsv3b45aMtAHb/TwRsv3b7+rMdAjInewRsv3b5X48VA77DTwRsv3b4skcJAInvewRsv3b7pscBAu6LTwRsv3b5sc71A72zewRsv3b46lLtAiJTTwRsv3b6uVbhAvF7ewRsv3b58drZAVIbTwRsv3b7yN7NAh1DewRsv3b6vWLFAIHjTwRsv3b40Gq5AU0LewRsv3b7xOqxADWrTwRsv3b47B6lAxjLewRsv3b7MrqZA1VTTwRsv3b4C9qNAtRvewRsv3b5eIqFAVD/TwRsv3b4K2p5AKwbewRsv3b5cBJxA0ynTwRsv3b7ivZlAq/DdwRsv3b5E6JZAUhTTwRsv3b69oZRAKdvdwRsv3b4PzJFA0P7SwRsv3b6XhY9Ap8XdwRsv3b7pr4xAZ+nSwRsv3b7fbopAMq/dwRsv3b4eXIdAgtDSwRsv3b7SX4VAspPdwRsv3b5qxoFAK7PSwRsv3b6zTIBAwXTdwRsv3b6W03hAr5XSwRsv3b4XZnZARVfdwRsv3b4moG5AMnjSwRsv3b7GMmxAyDndwRsv3b7VbGRAtlrSwRsv3b51/2FATBzdwRsv3b6EOVpAST3SwRsv3b7N0VdAJf7cwRsv3b5BzE9APx7SwRsv3b5Buk1Aa9vcwRsv3b7cjURAUPjRwRsv3b6/okNAP7LcwRsv3b43jzlAF9LRwRsv3b6wdjlABozcwRsv3b4oYy9A3qvRwRsv3b6iSi9AzWXcwRsv3b4aNyVApYXRwRsv3b6UHiVAlD/cwRsv3b4MCxtAiV/RwRsv3b4r+hpALxjcwRsv3b6QkBBAOzfRwRsv3b6U7RBAqurbwRsv3b5aQAVAywfRwRsv3b7H4AZABbfbwRsv3b5gjPQ//NfQwRsv3b7Iffk/NIfbwRsv3b5YSOA/K6jQwRsv3b72OeU/ZVfbwRsv3b7IBMw/XHjQwRsv3b4q9tA/lifbwRsv3b79wLc/6UjQwRsv3b5T2Lw/EfTawRsv3b4W+KE/cRPQwRsv3b5L5Kg/O7fawRsv3b47PYs/X9nPwRsv3b5x1ZQ/AnrawRsv3b4QJGw/B5/PwRsv3b5XrYA/qD/awRsv3b5a00M/rWTPwRsv3b5uClk/TgXawRsv3b4agxs/fyrPwRsv3b6Q1zA/PMnZwRsv3b5gC+Q+jO7OwRsv3b44Igk/x4PZwRsv3b6WYog+U6nOwRsv3b4+E8M+bTbZwRsv3b5MQL49YWPOwRsv3b7SDWY+e/DYwRsv3b4I8YG9bx3OwRsv3b5Q6os9iarYwRsv3b4wEWG+YtfNwRsv3b6TwrS9q2XYwRsv3b5FWL++rJHNwRsv3b7dL32+4SvYwRsv3b6qGwG/qljNwRsv3b5HftC+mfzXwRsv3b62ciO/hSDNwRsv3b7bmxC/dMTXwRsv3b7vz0u/YOjMwRsv3b6f+Di/T4zXwRsv3b6zLHS/O7DMwRsv3b5iVWG/KlTXwRsv3b68RI6/bnfMwRsv3b7bGIW/xyLXwRsv3b5O5Z+/OEnMwRsv3b7Dr5m/p/zWwRsv3b4LCrG/Ax7MwRsv3b6XDK6/0NLWwRsv3b5UxMS/7PLLwRsv3b75WsK/uafWwRsv3b62Etm/1sfLwRsv3b5aqda/o3zWwRsv3b4XYe2/h5zLwRsv3b7MEuu/yVPWwRsv3b4LTQDAbXXLwRsv3b45s/+/tDLWwRsv3b7l/QjAWlbLwRsv3b5fIwrA8BbWwRsv3b5xQBLAkzfLwRsv3b64VRTAKPjVwRsv3b7schzAyxjLwRsv3b4UiB7AYNnVwRsv3b5IpSbABPrKwRsv3b5zuijAmrrVwRsv3b6F1zDA9drKwRsv3b58BjPAtZ7VwRsv3b69AjrAb8TKwRsv3b7VYD3AtovVwRsv3b5C1kLAK7HKwRsv3b5Oo0fAOHnVwRsv3b4is0zA953KwRsv3b693FHAA2bVwRsv3b6y7FbAw4rKwRsv3b4yFlzA0FLVwRsv3b4GJmHAkHfKwRsv3b6jT2bAnT/VwRsv3b53X2vAKmTKwRsv3b5Gp3DAYS7VwRsv3b5sY3TA5VrKwRsv3b65BHvAtibVwRsv3b7uUX3AjlLKwRsv3b4booLAdh7VwRsv3b5Su4PAOErKwRsv3b6nwIfAIBbVwRsv3b7e2YjA40HKwRsv3b4034zAyw3VwRsv3b5r+I3AjTnKwRsv3b7B/ZHAdQXVwRsv3b74FpPAJzHKwRsv3b5eKpfAyP3UwRsv3b5cpZfALzLKwRsv3b6SWJzA9f7UwRsv3b6rJ5zACjTKwRsv3b6+eKHAzADVwRsv3b4iOqHA5TXKwRsv3b6xl6bApwLVwRsv3b4VWabAwDfKwRsv3b6mtqvAggTVwRsv3b75d6vAnDnKwRsv3b6a1bDAXgbVwRsv3b7tlrDAfzvKwRsv3b7I/rXA8AfVwRsv3b4UTbXAQUTKwRsv3b6AK7vAwg/VwRsv3b672LnAsU/KwRsv3b7DTcDA0RrVwRsv3b4Tzb7AFlvKwRsv3b7wa8XANybVwRsv3b5R68PAfWbKwRsv3b4gisrAnTHVwRsv3b5vCcnA5HHKwRsv3b5OqM/ABD3VwRsv3b6dJ87AU33KwRsv3b6QytTAB0jVwRsv3b77G9PAmYvKwRsv3b6g9dnAlFTVwRsv3b47sNfAEqDKwRsv3b5kG9/AimfVwRsv3b4fbdzAabTKwRsv3b7ZN+TA4XvVwRsv3b6DieHAwMjKwRsv3b5LVOnAOJDVwRsv3b71pebAF93KwRsv3b68cO7Aj6TVwRsv3b53wuvAbfHKwRsv3b4ujfPA5bjVwRsv3b7p3vDAwwXLwRsv3b6EqfjAQc3VwRsv3b5//PXABhrLwRsv3b7Qxf3Ai+HVwRsv3b61GvvAPy7LwRsv3b4fcQHBx/XVwRsv3b7VGwDBekLLwRsv3b5c/wPBAgrWwRsv3b4SqgLBtFbLwRsv3b6YjQbBPB7WwRsv3b5WOAXB7mrLwRsv3b7UGwnBdjLWwRsv3b6TxgfBKH/LwRsv3b76qQvBt0bWwRsv3b6+VQrBQpPLwRsv3b4fOA7B21rWwRsv3b4j5QzBVqfLwRsv3b5bxhDB8W7WwRsv3b6Scw/Ba7vLwRsv3b6dVBPBBoPWwRsv3b7MARLBgM/LwRsv3b7f4hXBG5fWwRsv3b4OkBTBlOPLwRsv3b4BcRjBOavWwRsv3b6RHxfBfvfLwRsv3b4h/xrBML/WwRsv3b5grxnBXwvMwRsv3b5ijR3BFNPWwRsv3b7sPRzBQR/MwRsv3b6qGyDB9ubWwRsv3b40zB7BIjPMwRsv3b7SqSLB4vrWwRsv3b62WyHB2kbMwRsv3b7nNyXBrA7XwRsv3b5E7CPBcFrMwRsv3b4exifBSyLXwRsv3b6aeybBB27MwRsv3b5nVCrB5TXXwRsv3b43CinBkYHMwRsv3b5g4izBjEnXwRsv3b4UnCvBsJTMwRsv3b5ucC/BxlzXwRsv3b6xLS7Bz6fMwRsv3b6L/jHB+m/XwRsv3b6svjDBlLrMwRsv3b5SjDTB9ILXwRsv3b6RUzPB1czMwRsv3b6eGTfBkpXXwRsv3b4D7jXB7d3MwRsv3b6NojnBNKjXwRsv3b5ytjjBdujMwRsv3b62HzzBNLXXwRsv3b47/DvBH+LMwRsv3b4zlz7BeqzXwRsv3b6hfz/B3M3MwRsv3b7YDkHBBJDXwRsv3b6Z80LBv6rMwRsv3b5sgkPB2V3XwRsv3b6fckbBFXjMwRsv3b6k8EXBhRTXwRsv3b5i9knBjDXMwRsv3b54V0jBgbLWwRsv3b7Afk3BNOLLwRsv3b7/tErBUjbWwRsv3b7eBFHBOX7LwRsv3b6LB03B0Z/VwRsv3b4rg1TBIgnLwRsv3b6wTE/BGe7UwRsv3b4s9VfBN4PKwRsv3b71glHBgyLUwRsv3b72UVvB2uzJwRsv3b46qFPBTz3TwRsv3b4nl17BREbJwRsv3b74ulXB30DSwRsv3b7Gu2HBopDIwRsv3b4fulfB/C7RwRsv3b53vmTBPMzHwRsv3b4SpFnBMwrQwRsv3b5mmWfBn/rGwRsv3b7+eFvBltXOwRsv3b7NS2rBVxzGwRsv3b56N13B1ZLNwRsv3b6K1GzBuzLFwRsv3b6C4F7BQUbMwRsv3b7UMW/B6T7EwRsv3b6Sc2DBx+/KwRsv3b4saHHBpULDwRsv3b6f72HBjIjJwRsv3b7RhHPB3TrCwRsv3b7BU2PBbSnIwRsv3b6eYHXBvynBwRsv3b4EsWTBofbGwRsv3b5z6XbBeRXAwRsv3b5GD2bBStzFwRsv3b5pT3jBtgC/wRsv3b4ybWfBgsTEwRsv3b4nsXnBl+u9wRsv3b6GymjBzK3DwRsv3b58EHvBcNa8wRsv3b7kJ2rBVZfCwRsv3b6Cb3zBH8G7wRsv3b6XhGvBXIDBwRsv3b5Kzn3BpKu6wRsv3b5I4WzBjGrAwRsv3b5lK3/BKJa5wRsv3b74PW7BulS/wRsv3b5BRIDBsIC4wRsv3b5rmm/BNz6+wRsv3b4i84DBFWu3wRsv3b6T9nDB6Ce9wRsv3b6noYHBalW2wRsv3b7QUnLBPxK8wRsv3b7ET4LBwT+1wRsv3b4Kr3PBi/y6wRsv3b7o/YLBJCq0wRsv3b4tC3XBN+a5wRsv3b5trIPBbRSzwRsv3b4AZ3bB1M+4wRsv3b7CWoTBp/6xwRsv3b7lwnfBELq3wRsv3b6zCIXB4uiwwRsv3b7LHnnBS6S2wRsv3b6mtoXBHdOvwRsv3b6wenrBgo61wRsv3b6aZIbBYr2uwRsv3b6G1nvBQni0wRsv3b7ZEofBlKetwRsv3b4dMn3B8GGzwRsv3b73wIfBupGswRsv3b7EjX7BGEyywRsv3b7KbojB4XurwRsv3b5q6X/BPjaxwRsv3b6dHInBB2aqwRsv3b6IooDBYiCwwRsv3b5yyonBL1CpwRsv3b5aUIHBeQqvwRsv3b5OeIrBXDqowRsv3b4j/oHBQfStwRsv3b5WJovBeiSnwRsv3b7aq4LBCt6swRsv3b5C1IvBkQ6mwRsv3b6VWYPBIcirwRsv3b79gYzBqPikwRsv3b5QB4TBN7KqwRsv3b64L43BwOKjwRsv3b4KtYTBT5ypwRsv3b5y3Y3B18yiwRsv3b7FYoXBZ4aowRsv3b4ti47BxbehwRsv3b76D4bB42enwRsv3b5DPo/B/6GgwRsv3b6HuobBA0GmwRsv3b5S84/BRoqfwRsv3b7KY4fBpCGlwRsv3b47oZDB4HGewRsv3b53DYjBOAmkwRsv3b7rSpHBeFmdwRsv3b4jt4jB0PCiwRsv3b6X9JHBEEGcwRsv3b7QYInBbtihwRsv3b5BnpLBQCmbwRsv3b4iCorB/7mgwRsv3b6RS5PBqRGawRsv3b4ysYrBu46fwRsv3b5Z/pPBpveYwRsv3b6FVYvBnmeewRsv3b5bqpTBedyXwRsv3b6G+ovBcUydwRsv3b5cT5XBTMGWwRsv3b6In4zBRzGcwRsv3b5c9JXBH6aVwRsv3b6KRI3BFxabwRsv3b5gmZbBA4yUwRsv3b7s6I3B9++ZwRsv3b65RJfBPXGTwRsv3b7qiY7BTr6YwRsv3b6/8pfB8FOSwRsv3b7zKI/BJJaXwRsv3b7gl5jBrDWRwRsv3b6IyI/B3XeWwRsv3b52N5nBZxeQwRsv3b4caJDBmFmVwRsv3b4K15nBbPmOwRsv3b6HB5HBVziUwRsv3b5IeJrBHd2NwRsv3b4EpZHBMAGTwRsv3b5+JJvBfb2MwRsv3b4YPZLB48mRwRsv3b48yZvBwZuLwRsv3b5M1pLBJKiQwRsv3b5yYpzBBnqKwRsv3b5/b5PBaYaPwRsv3b6l+5zBoliJwRsv3b6FCJTBK2GOwRsv3b6zlp3BRDmIwRsv3b5Un5TB5yGNwRsv3b4MPp7BKBaHwRsv3b6wL5XBlOKLwRsv3b6a3J7BivCFwRsv3b5OwZXB87yKwRsv3b46bp/B7MqEwRsv3b7sUpbBWJeJwRsv3b7W/5/BFKeDwRsv3b6s45bBh1+IwRsv3b5kmqDBoIKCwRsv3b7lbpfBdxSHwRsv3b4COKHBTFqBwRsv3b6Z9pfBhNmFwRsv3b5TyKHBUTCAwRsv3b4Qf5jBiK+EwRsv3b7KUKLBTw9+wRsv3b7uBpnBC3iDwRsv3b5l36LBqsB7wRsv3b4DipnBeiGCwRsv3b4/d6PBt2h5wRsv3b4LBprB99SAwRsv3b77AKTB/Ap3wRsv3b5Mg5rBM0x/wRsv3b48fqTBfrJ0wRsv3b57/5rBurh8wRsv3b5qBqXB0FlywRsv3b6qc5vB+Ol5wRsv3b5xkaXBgPZvwRsv3b704ZvBi0t3wRsv3b6WCqbB741twRsv3b41UZzB+uJ0wRsv3b7XeabBZCVrwRsv3b51wJzBK3pywRsv3b4k6abB0rxowRsv3b6tL53BXxFwwRsv3b5mWKfBPVRmwRsv3b7nnp3BwqhtwRsv3b6hx6fBrutjwRsv3b4dDp7B1j9rwRsv3b7pNqjBFoNhwRsv3b5LfZ7B+9ZowRsv3b4jpqjBfxpfwRsv3b567J7BIW5mwRsv3b5eFanB57FcwRsv3b6fW5/BCQVkwRsv3b6ahKnBSUlawRsv3b6+yp/B9ZthwRsv3b7O86nBq+BXwRsv3b7VOaDBqDJfwRsv3b4EY6rBEHhVwRsv3b7cqKDB28hcwRsv3b5C0qrBcw9TwRsv3b7JF6HBVV5awRsv3b6HQavBEadQwRsv3b6EhqHBWvBXwRsv3b5EsavB/ZROwRsv3b6S6aHBjwlSwRsv3b7EkqzBx4dMwRsv3b5K4KHB5JpKwRsv3b4VoqzBXiFKwRsv3b44r6HBmMBGwRsv3b7/WazBSppHwRsv3b7Ze6HBLzZEwRsv3b5dJqzBHhNFwRsv3b5ZSKHBI61BwRsv3b638qvB6ItCwRsv3b69FKHB1iQ/wRsv3b4Fv6vBqwRAwRsv3b4a4aDB8Jw8wRsv3b5Ui6vBbn09wRsv3b5praDBLRU6wRsv3b6ZV6vBMPY6wRsv3b62eaDBgY03wRsv3b7dI6vB9W44wRsv3b74RaDB4QU1wRsv3b4W8KrBuOc1wRsv3b44EqDBWH4ywRsv3b5RvKrBf2AzwRsv3b5x3p/ByvYvwRsv3b6CiKrBa9owwRsv3b6/qp/Bf2MtwRsv3b7AU6rByFwuwRsv3b4vdp/B+H8qwRsv3b5rFqrBuOErwRsv3b4UN5/Bd6wnwRsv3b7nzqnBG2MpwRsv3b6r957BQv0kwRsv3b6OiqnBC+wmwRsv3b4Ys57BBB0iwRsv3b6DOqnBmHMkwRsv3b5fZ57BEWwfwRsv3b4u6KjBRP4hwRsv3b4SHJ7BLpscwRsv3b6BkajBCpAfwRsv3b45xp3B+MAZwRsv3b4rLajBeR0dwRsv3b6vbZ3B+h4XwRsv3b7lzafBwrIawRsv3b5rEp3BVkUUwRsv3b76YafBHUsYwRsv3b5LrZzBro0RwRsv3b4M8KbB6OMVwRsv3b5MSJzBjNEOwRsv3b65fKbBOYYTwRsv3b7O2ZvBNwMMwRsv3b4I+qXBFSYRwRsv3b4jZ5vBx2gJwRsv3b5dfKXBys0OwRsv3b728ZrB/J0GwRsv3b578KTB1noMwRsv3b5WcprBJvcDwRsv3b5GX6TBvigKwRsv3b758pnBdkcBwRsv3b5Ty6PBk+IHwRsv3b4faZnBUB39wBsv3b7VJqPBgJoFwRsv3b6825jBcgj4wBsv3b5IiaLBI1wDwRsv3b5JSpjBkqXywBsv3b5+2qHBYyIBwRsv3b6Br5fBtqXtwBsv3b76LKHBRdb9wBsv3b58FZfBsnLowBsv3b6ed6DBPoL5wBsv3b4FcJbBWFnjwBsv3b5dtZ/BcyX1wBsv3b4SyZXBvJ7ewBsv3b49AJ/Bq93wwBsv3b4JHpXBWaLZwBsv3b4EOZ7BRJvswBsv3b5pbJTBBAbVwBsv3b7leJ3BY17owBsv3b68u5PBCDHQwBsv3b7+rpzBxjrkwBsv3b5dApPBJGrLwBsv3b6E2ZvBOw/gwBsv3b6DRpLBBQzHwBsv3b6+FJvB8PLbwBsv3b4PipHBHFrCwBsv3b5BPZrBj+bXwBsv3b5qxpDBst69wBsv3b7sZJnBLdbTwBsv3b4LApDBRIy5wBsv3b78k5jBFNjPwBsv3b4HO4/BkQa1wBsv3b6NspfBEOHLwBsv3b68bo7BEMqwwBsv3b5k2JbBrerHwBsv3b6Zoo3BHIeswBsv3b60/JXBXgfEwBsv3b7a0ozBnCiowBsv3b5VE5XBlybAwBsv3b7o/ovB9BikwBsv3b5vNZTBJ0i8wBsv3b57K4vBUfGfwBsv3b4+UpPBSHy4wBsv3b6KVIrBS7WbwBsv3b6ZYpLBMrK0wBsv3b7VeYnB6cOXwBsv3b4Tf5HB3+mwwBsv3b6Kn4jBe8CTwBsv3b5cl5DBQDKtwBsv3b6vwofBhqKPwBsv3b7Moo/B/X6pwBsv3b4J4obB3sOLwBsv3b7qt47BrselwBsv3b5vAIbBrg6IwBsv3b7Q1o3BmQ+iwBsv3b7iHoXBClyEwBsv3b6O9ozB+FaewBsv3b6VPYTBsqaAwBsv3b4HFozBSZ6awBsv3b5CXIPBAuR5wBsv3b6mNYvBauSWwBsv3b4ce4LBW35ywBsv3b4qVorBJCqTwBsv3b40moHBGRJrwBsv3b48donBIW+PwBsv3b5AuYDBCq9jwBsv3b6Dl4jBVLKLwBsv3b6fsX/BAkdcwBsv3b4juYfBAvWHwBsv3b6Y8H3BbmRXwBsv3b6hhobBQDSEwBsv3b7fMHzBEqlTwBsv3b5cLoXB93GAwBsv3b7ccXrBCuhPwBsv3b4e1oPBEFR5wBsv3b5ss3jBtkFMwBsv3b6AgoLBvZhxwBsv3b7B8nbBb0hJwBsv3b6cRYHBlTxpwBsv3b5KNnXBYgdIwBsv3b5GQ4DBTtxfwBsv3b4NtHPBa81CwBsv3b7nSIDBWt9VwBsv3b5YenLB3R0/wBsv3b73AIDBy2NLwBsv3b4cjXHBzqk6wBsv3b6v7n/BPJRAwBsv3b7k7XDBRAk2wBsv3b5TFYDBT9o1wBsv3b7rnHDBfUYvwBsv3b7YqYDBz3QrwBsv3b65Z3DB7x8mwBsv3b63PYHB7CohwBsv3b4uO3DBbyccwBsv3b760oHB6+cWwBsv3b7PEnDB8vgRwBsv3b60aYLBY6cMwBsv3b4m7W/Bt9QHwBsv3b4Qv4LBkmcCwBsv3b5zyW/B+qr7vxsv3b6nrYLBl1Hwvxsv3b6qp2/BLY7nvxsv3b4TnYLBbNXbvxsv3b7ehm/B2l7Tvxsv3b7qjILBL1bHvxsv3b5cZ2/BMUm/vxsv3b56fYLBB9Kyvxsv3b6oSW/BVF6rvxsv3b4Ob4LBo0Wevxsv3b6kLm/BWL2Xvxsv3b4lYoLBTKmJvxsv3b7YF2/BjrGEvxsv3b6UV4LBLYBpvxsv3b6aCG/BgE1pvxsv3b4aUYLByb4+vxsv3b65F2/Bu+BSvxsv3b73U4LBx7cTvxsv3b4tWW/ByDFBvxsv3b5ZYYLBTBbSvhsv3b7UzW/BoYcuvxsv3b56MILBncZ7vhsv3b6jd3DBEIAZvxsv3b4LyIHBHICuvRsv3b6aWHHBfdoKvxsv3b6zGoLBaX+QPRsv3b5PbnLBC4Puvhsv3b5OC4LBxUNhPhsv3b4luHPBAyDhvhsv3b7/yYLB0H25Phsv3b5mMHXB9HDJvhsv3b74NIPBF1T+Phsv3b7A03bBTRa4vhsv3b5Zw4PBcUIfPxsv3b7jmHjBNDKXvhsv3b6+OITBrY49Pxsv3b7db3rBkIJevhsv3b5z1ITB7sJaPxsv3b41V3zB0PwPvhsv3b7ed4XBcZh2Pxsv3b50U37BMNCMvRsv3b4eIIbBQ5GIPxsv3b5cL4DBAL1FOxsv3b5G1YbBCViVPxsv3b6DP4HB4Gt7PRsv3b7EcofB6B+hPxsv3b43YILBkDHRPRsv3b4E8ofB35WrPxsv3b7kjoPBIMAKPhsv3b5kbYjBMp60Pxsv3b7SyYTB6BFVPhsv3b5pwYjBHha8Pxsv3b5yD4bBUMlgPhsv3b5mS4nBKvPBPxsv3b5NXYfBKDd3Phsv3b69zYnBnirGPxsv3b4lsYjBmDpcPhsv3b4kZYrBesXIPxsv3b50CIrBaLFkPhsv3b6N8YrB887JPxsv3b57YYvBSCtoPhsv3b4Og4vBHU7JPxsv3b7tt4zB2KplPhsv3b7ZMYzBa8bHPxsv3b5jBo7BWCtcPhsv3b7BKo3BSgLGPxsv3b4VT4/BsA9PPhsv3b4tW47B1yTEPxsv3b56lpDBoKdAPhsv3b48l4/BgzfCPxsv3b6B3ZHBMKAxPhsv3b4J1pDBgDvAPxsv3b49JJPBgAkiPhsv3b7ZFpLBXDi+Pxsv3b6wapTBQBkSPhsv3b4OWpPBWDC8Pxsv3b4lsZXBmAcCPhsv3b7fnJTBkh+6Pxsv3b5j95bBQDTjPRsv3b4z4ZXBDhG4Pxsv3b5TPZjB4CDCPRsv3b7PKJfBSAe2Pxsv3b4yg5nBQD6hPRsv3b5rcZjBXwO0Pxsv3b4RyZrBkK+APRsv3b50upnBzQWyPxsv3b7fDpzBYNZAPRsv3b6OBJvBwxKwPxsv3b51VJ3BQAgBPRsv3b6cUZzB2jKuPxsv3b5+mZ7BgKmEPBsv3b5/pZ3BDoKsPxsv3b4K1p/BAAhHuhsv3b7LUp/BrkOsPxsv3b6R/aDBAGCiORsv3b763qHBqFCvPxsv3b7eE6LBUOuHPRsv3b7q5KTBvIy1Pxsv3b64IKPBSDdDPhsv3b6LnqfBIuC+Pxsv3b4ZLqTBjBigPhsv3b6hcqnBg+nIPxsv3b6VSaXBQOrKPhsv3b4SoarBkw7TPxsv3b58ZabBOH30Phsv3b7yw6vB3D7dPxsv3b5qgafBIt0OPxsv3b4/46zB8HPnPxsv3b42najBpn4jPxsv3b4NAq7BorDxPxsv3b7XuKnBOB84Pxsv3b7NH6/BC+/7Pxsv3b591KrB9rVMPxsv3b7ZPLDB6BgDQBsv3b4C8KvBqlFhPxsv3b6RWbHBiToIQBsv3b6GC63BDux1Pxsv3b4odrLBCl4NQBsv3b7qJq7BGEaFPxsv3b6FkrPBh4ESQBsv3b5NQq/Bj5aPPxsv3b7urrTBxaYXQBsv3b6CXbDBne6ZPxsv3b6Yy7XB1swcQBsv3b6neLHBs0ikPxsv3b486LbBvvQhQBsv3b6dk7LBCqquPxsv3b4UBbjB7R0nQBsv3b5nrrPBPRS5Pxsv3b5qIrnBR0osQBsv3b7uyLTBVIfDPxsv3b7CP7rBFngxQBsv3b4t47XB2QrOPxsv3b5AXrvBsKo2QBsv3b68/LbBwLLYPxsv3b7cfrzB/OI7QBsv3b5kFbjBXIzjPxsv3b6Hor3BTxtBQBsv3b5QKrnBbpTvPxsv3b4b4b7BkmxGQBsv3b7QMrrB7FD/Pxsv3b4IZcDBAERMQBsv3b67KLvBNn0JQBsv3b4nA8LBS6pSQBsv3b7rD7zBFEcUQBsv3b6jiMPBypdZQBsv3b7x5bzBICQgQBsv3b7v9sTBYgZhQBsv3b4Tqb3B7QMtQBsv3b7WSMbBHe5oQBsv3b7vV77BDL46QBsv3b6EeMfBKkRxQBsv3b7J8b7BuhNJQBsv3b5FgcjBT/p5QBsv3b6Ddr/Bv8hXQBsv3b6UYcnBfnuBQBsv3b7i5b/BvhdnQBsv3b7fHsrBjiqGQBsv3b5hPsDBpIR1QBsv3b4NqcrBIAuLQBsv3b7/jMDBjaGAQBsv3b5JCMvBCQCQQBsv3b6e2sDBCsmFQBsv3b4DWcvB6veUQBsv3b6TJ8HB09qKQBsv3b6Kp8vBMPGZQBsv3b7Wc8HBW+aPQBsv3b7l9MvBX+ueQBsv3b7Kv8HBReyUQBsv3b6NQczBL+ajQBsv3b5pC8LBdO+ZQBsv3b6qjczBJuGoQBsv3b7jVsLBqPKeQBsv3b6h2czBptytQBsv3b4IosLBBvSjQBsv3b4fJc3BS9iyQBsv3b4u7cLB8/OoQBsv3b6GcM3BAtS3QBsv3b4SOMPBzPWtQBsv3b7Gu83BNdC8QBsv3b7egsPB2POyQBsv3b6uBs7BR8zBQBsv3b6qzcPBRvO3QBsv3b6sUc7BaMjGQBsv3b5AGMTBJfS8QBsv3b6InM7B68TLQBsv3b7GYsTB+vHBQBsv3b4i587BP7zQQBsv3b4ArcTB6STHQBsv3b7MNM/BNa3VQBsv3b4t88TBv7vMQBsv3b4VhM/B7qjaQBsv3b5iM8XB0iHSQBsv3b7Iyc/BHK7fQBsv3b4VdMXBACfXQBsv3b57CtDBsqXkQBsv3b4ftMXBdrfcQBsv3b7vUdDBPKPpQBsv3b4a68XB3lbiQBsv3b4pkNDBw67uQBsv3b4uIsbBHmvnQBsv3b6dx9DBabTzQBsv3b4DWcbBs7vsQBsv3b6TAdHBgLX4QBsv3b4hi8bBKlryQBsv3b7WOdHBS8D9QBsv3b6luMbBaLf3QBsv3b5WatHBNWkBQRsv3b5u5sbBd8n8QBsv3b4fmNHB0+sDQRsv3b7JE8fB0C8BQRsv3b5XytHBRnAGQRsv3b6QOMfBtAAEQRsv3b6689HBS/sIQRsv3b5GXcfBjY8GQRsv3b6nGNLBRoQLQRsv3b7ggcfBOjMJQRsv3b67PtLBrwkOQRsv3b4bo8fBowIMQRsv3b6zY9LBlJIQQRsv3b7hvsfBy7oOQRsv3b6ZgdLBrR8TQRsv3b7Y2sfB5UcRQRsv3b6QndLBRKgVQRsv3b6f9sfBMwMUQRsv3b5lu9LB3y8YQRsv3b5ZDMjBZNIWQRsv3b6f09LBWLwaQRsv3b7FH8jBtXMZQRsv3b6u59LBrUodQRsv3b5AM8jBAgIcQRsv3b4p+9LB69IfQRsv3b6RRsjBtc4eQRsv3b5OENPB3lsiQRsv3b7OUcjBx5whQRsv3b70HNPB6OokQRsv3b4YXcjBeiwkQRsv3b5BKNPBzHgnQRsv3b5caMjB+McmQRsv3b6+M9PBDAIqQRsv3b71ccjB2JMpQRsv3b41PtPBxowsQRsv3b5PdcjBq1MsQRsv3b72QdPBOhwvQRsv3b6xeMjBJ+MuQRsv3b5YRdPBrqsxQRsv3b4TfMjBm3IxQRsv3b66SNPBITs0QRsv3b51f8jBFgI0QRsv3b4cTNPBk8o2QRsv3b7WgsjBkZE2QRsv3b59T9PBB1o5QRsv3b43hsjBBSE5QRsv3b7eUtPBeuk7QRsv3b6YicjBibA7QRsv3b4/VtPB7Hg+QRsv3b73jMjBDEA+QRsv3b6eWdPBXwhBQRsv3b5WkMjBf89AQRsv3b79XNPB0pdDQRsv3b61k8jB+15DQRsv3b5cYNPBRSdGQRsv3b4Tl8jBfu5FQRsv3b66Y9PBt7ZIQRsv3b5wmsjBCX5IQRsv3b4XZ9PBKEZLQRsv3b7LncjBlA1LQRsv3b5yatPBmdVNQRsv3b4locjBL51NQRsv3b7NbdPBB2VQQRsv3b57pMjB4CxQQRsv3b4jcdPBavRSQRsv3b7Np8jBBb1SQRsv3b52dNPBxCxVQRsv3b5+rsjBP8lYQRsv3b5hVNPB/CBXQRsv3b6PUMjBum9fQRsv3b6wSNLBA1tZQRsv3b7CyMfBFaFiQRsv3b7FidHB9qpbQRsv3b4KPMfBZfFkQRsv3b73/NDB6/pdQRsv3b5Nr8bBjUFnQRsv3b4ucNDB3kpgQRsv3b6OIsbBopFpQRsv3b5n48/B0JpiQRsv3b7NlcXBtOFrQRsv3b6eVs/BwupkQRsv3b4JCcXBtzFuQRsv3b7Wyc7BtDpnQRsv3b5FfMTBq4FwQRsv3b4SPc7BpYppQRsv3b6B78PBrdFyQRsv3b5KsM3BlNprQRsv3b68YsPBrCF1QRsv3b6AI83BhSpuQRsv3b721cLBnXF3QRsv3b66lszBdnpwQRsv3b4wScLBlsF5QRsv3b7zCczBZcpyQRsv3b5pvMHBjRF8QRsv3b4qfcvBbxh1QRsv3b4WMMHB9HR+QRsv3b6768rBF1J3QRsv3b7UpcDBy8WAQRsv3b5AKcrBEXV5QRsv3b4BBMDB9ziCQRsv3b5bUMnBaal7QRsv3b5IXb/BI1ODQRsv3b6iqcjB09d9QRsv3b5XuL7BrYuEQRsv3b6u8MfBtPN/QRsv3b52Dr7BXuuFQRsv3b7zEsfBZQOBQRsv3b5JV73B7SSHQRsv3b5mO8bBNhGCQRsv3b47nbzBvjKIQRsv3b5YgcXBrB2DQRsv3b4e5LvBc06JQRsv3b6kvcTBViOEQRsv3b6CKrvBjpKKQRsv3b4M18PBPSKFQRsv3b7eZLrBAsCLQRsv3b4b7sLBRyWGQRsv3b4WnLnBDMOMQRsv3b5TJcLBUyiHQRsv3b5O07jBFcaNQRsv3b6MXMHBwyiIQRsv3b6RDLjBz+OOQRsv3b7MfsDB8iGJQRsv3b5vQLfBSg2QQRsv3b5Gi7/B+RmKQRsv3b6ebbbBfxiRQRsv3b5OqL7BtBOLQRsv3b5dmbXBOhKSQRsv3b4N1L3Bbw2MQRsv3b4dxbTB8wuTQRsv3b7P/7zBkwWNQRsv3b468rPB+hWUQRsv3b6XHbzBMPiNQRsv3b7NHLPBEi+VQRsv3b5EJrvBDeiOQRsv3b4XQbLBeDOWQRsv3b7uN7rBvdmPQRsv3b65Y7HBKCWXQRsv3b6QWrnBbcuQQRsv3b5bhrDB2haYQRsv3b4wfbjBHL2RQRsv3b79qK/BiQiZQRsv3b7Sn7fBhKySQRsv3b69za7BnRGaQRsv3b7CrLbBvpWTQRsv3b737K3B1xmbQRsv3b5orrXB9X+UQRsv3b6zCK3BqQmcQRsv3b6xxLTBq2qVQRsv3b7zI6zBX/ScQRsv3b7x37PBYVWWQRsv3b4zP6vBFd+dQRsv3b4x+7LBF0CXQRsv3b5zWqrBy8meQRsv3b5xFrLBRSmYQRsv3b40d6nBMMSfQRsv3b5FIrHBqQ2ZQRsv3b6xkKjBJcKgQRsv3b4RIrDBivGZQRsv3b6jpqfBfa6hQRsv3b5bL6/BLNaaQRsv3b7Qu6bBH5OiQRsv3b6IRK7Bz7qbQRsv3b790KXBwHejQRsv3b63Wa3Bcp+cQRsv3b4q5qTBY1ykQRsv3b7kbqzBE4SdQRsv3b5X+6PBBkGlQRsv3b4PhKvB/3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+//3///wAA/7//f///AAD/v/9///8AAP+/") }] diff --git a/world/interactables/fishing_shop_world.tscn b/world/interactables/fishing_shop_world.tscn index 0f4ed84..7221005 100644 --- a/world/interactables/fishing_shop_world.tscn +++ b/world/interactables/fishing_shop_world.tscn @@ -1,90 +1,28 @@ -[gd_scene load_steps=11 format=3] +[gd_scene load_steps=5 format=3] [ext_resource type="Script" path="res://world/fishing_shop_interaction.gd" id="1_shop"] -[ext_resource type="Script" path="res://world/world_prop.gd" id="2_prop"] - -[sub_resource type="BoxMesh" id="CounterMesh"] -resource_local_to_scene = true -size = Vector3(4.2, 1.25, 1.8) - -[sub_resource type="BoxMesh" id="RoofMesh"] -resource_local_to_scene = true -size = Vector3(5.2, 0.35, 3.2) - -[sub_resource type="BoxMesh" id="PostMesh"] -resource_local_to_scene = true -size = Vector3(0.28, 3.2, 0.28) - -[sub_resource type="BoxMesh" id="BackMesh"] -resource_local_to_scene = true -size = Vector3(5, 2.8, 0.25) - -[sub_resource type="StandardMaterial3D" id="PrimaryMaterial"] -resource_local_to_scene = true -albedo_color = Color(0.12, 0.68, 0.62, 1) - -[sub_resource type="StandardMaterial3D" id="AccentMaterial"] -resource_local_to_scene = true -albedo_color = Color(1, 0.68, 0.18, 1) - -[sub_resource type="StandardMaterial3D" id="BackMaterial"] -resource_local_to_scene = true -albedo_color = Color(0.13, 0.34, 0.38, 1) +[ext_resource type="PackedScene" path="res://art/exported/characters/base/netfishing_base_character.glb" id="2_character"] +[ext_resource type="Script" path="res://world/world_character_display.gd" id="3_display"] [sub_resource type="SphereShape3D" id="InteractionShape"] resource_local_to_scene = true radius = 3.0 -[sub_resource type="BoxShape3D" id="CounterShape"] -resource_local_to_scene = true -size = Vector3(4.2, 1.25, 1.8) - [node name="FishingShopWorld" type="Node3D"] -script = ExtResource("2_prop") -collision_root_path = NodePath("Collision") -interaction_area_path = NodePath("InteractionArea") -entrance_marker_path = NodePath("EntranceMarker") -[node name="Visual" type="Node3D" parent="."] +[node name="Shopkeeper" parent="." instance=ExtResource("2_character")] +script = ExtResource("3_display") +species_id = "pointy" +fur_color_id = "white" +ears_id = "pointy_wide" +tail_id = "fox" +eyes_id = "punk_purple" +nose_id = "triangle_small" +mouth_id = "slanted" +head_look_enabled = true -[node name="Counter" type="MeshInstance3D" parent="Visual"] -position = Vector3(0, 0.625, 0) -mesh = SubResource("CounterMesh") -material_override = SubResource("PrimaryMaterial") - -[node name="Back" type="MeshInstance3D" parent="Visual"] -position = Vector3(0, 1.4, 1.25) -mesh = SubResource("BackMesh") -material_override = SubResource("BackMaterial") - -[node name="Roof" type="MeshInstance3D" parent="Visual"] -position = Vector3(0, 3.15, 0.35) -mesh = SubResource("RoofMesh") -material_override = SubResource("AccentMaterial") - -[node name="WestPost" type="MeshInstance3D" parent="Visual"] -position = Vector3(-2.15, 1.6, -0.8) -mesh = SubResource("PostMesh") -material_override = SubResource("PrimaryMaterial") - -[node name="EastPost" type="MeshInstance3D" parent="Visual"] -position = Vector3(2.15, 1.6, -0.8) -mesh = SubResource("PostMesh") -material_override = SubResource("PrimaryMaterial") - -[node name="ShopName" type="Label3D" parent="Visual"] -position = Vector3(0, 3.7, 0) -text = "fishing shop" -font_size = 42 -outline_size = 9 -billboard = 1 -no_depth_test = true - -[node name="Collision" type="StaticBody3D" parent="."] - -[node name="CounterShape" type="CollisionShape3D" parent="Collision"] -position = Vector3(0, 0.625, 0) -shape = SubResource("CounterShape") +[node name="AnimationPlayer" parent="Shopkeeper"] +autoplay = "idle" [node name="InteractionArea" type="Area3D" parent="."] unique_name_in_owner = true @@ -95,6 +33,3 @@ script = ExtResource("1_shop") [node name="InteractionShape" type="CollisionShape3D" parent="InteractionArea"] shape = SubResource("InteractionShape") - -[node name="EntranceMarker" type="Marker3D" parent="."] -position = Vector3(0, 0, -2) diff --git a/world/materials/foliage_wind.gdshader b/world/materials/foliage_wind.gdshader new file mode 100644 index 0000000..a1220e7 --- /dev/null +++ b/world/materials/foliage_wind.gdshader @@ -0,0 +1,44 @@ +shader_type spatial; +render_mode cull_back, depth_draw_opaque; + +uniform vec4 albedo_color : source_color = vec4(1.0); +uniform float material_roughness = 1.0; +uniform float material_metallic = 0.0; +uniform float local_min_y = 0.0; +uniform float local_height = 1.0; +uniform float local_wind_strength = 0.08; +uniform float wind_speed = 0.9; +uniform float wind_phase = 0.0; + + +void vertex() { + float height_ratio = clamp( + (VERTEX.y - local_min_y) / max(local_height, 0.001), + 0.0, + 1.0 + ); + float bend_mask = smoothstep(0.08, 1.0, height_ratio); + vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; + float broad_wave = sin( + TIME * wind_speed + + dot(world_position.xz, vec2(0.23, 0.17)) + + wind_phase + ); + float leaf_flutter = sin( + TIME * wind_speed * 2.17 + + dot(world_position.xz, vec2(-0.41, 0.37)) + + wind_phase * 1.7 + ); + float displacement = ( + broad_wave * 0.72 + leaf_flutter * 0.28 + ) * local_wind_strength * bend_mask; + VERTEX.x += displacement; + VERTEX.z += displacement * 0.42; +} + + +void fragment() { + ALBEDO = albedo_color.rgb; + ROUGHNESS = material_roughness; + METALLIC = material_metallic; +} diff --git a/world/materials/foliage_wind.gdshader.uid b/world/materials/foliage_wind.gdshader.uid new file mode 100644 index 0000000..e5162a5 --- /dev/null +++ b/world/materials/foliage_wind.gdshader.uid @@ -0,0 +1 @@ +uid://c3q7l8m9n2v4x diff --git a/world/materials/shoreline_tide.gdshader b/world/materials/shoreline_tide.gdshader index b51f794..f8a58e0 100644 --- a/world/materials/shoreline_tide.gdshader +++ b/world/materials/shoreline_tide.gdshader @@ -1,16 +1,16 @@ shader_type spatial; render_mode blend_mix, depth_draw_never, cull_disabled, unshaded; -uniform vec4 lead_color : source_color = vec4(0.620, 0.875, 0.855, 1.0); -uniform vec4 trail_color : source_color = vec4(0.430, 0.780, 0.770, 1.0); -uniform float ribbon_width : hint_range(0.2, 2.0, 0.01) = 0.85; -uniform float land_inset : hint_range(0.0, 0.4, 0.01) = 0.10; +uniform vec4 lead_color : source_color = vec4(0.780, 0.960, 0.930, 1.0); +uniform vec4 trail_color : source_color = vec4(0.500, 0.840, 0.820, 1.0); +uniform float ribbon_width : hint_range(0.2, 2.0, 0.01) = 1.35; +uniform float land_inset : hint_range(0.0, 0.4, 0.01) = 0.12; uniform float tide_speed : hint_range(0.1, 3.0, 0.01) = 1.42; -uniform float tide_distance : hint_range(0.0, 0.6, 0.01) = 0.30; -uniform float lead_width : hint_range(0.01, 0.2, 0.005) = 0.065; -uniform float trail_width : hint_range(0.02, 0.4, 0.005) = 0.18; -uniform float lead_strength : hint_range(0.0, 1.0, 0.01) = 0.48; -uniform float trail_strength : hint_range(0.0, 1.0, 0.01) = 0.25; +uniform float tide_distance : hint_range(0.0, 0.6, 0.01) = 0.42; +uniform float lead_width : hint_range(0.01, 0.2, 0.005) = 0.14; +uniform float trail_width : hint_range(0.02, 0.4, 0.005) = 0.32; +uniform float lead_strength : hint_range(0.0, 1.0, 0.01) = 0.88; +uniform float trail_strength : hint_range(0.0, 1.0, 0.01) = 0.52; uniform float phase_scale : hint_range(0.001, 0.2, 0.001) = 0.035; uniform float phase_strength : hint_range(0.0, 2.0, 0.01) = 0.55; diff --git a/world/materials/shoreline_tide.tres b/world/materials/shoreline_tide.tres index dad0a9d..65d2fa8 100644 --- a/world/materials/shoreline_tide.tres +++ b/world/materials/shoreline_tide.tres @@ -5,15 +5,15 @@ [resource] render_priority = 1 shader = ExtResource("1_shader") -shader_parameter/lead_color = Color(0.619608, 0.87451, 0.854902, 1) -shader_parameter/trail_color = Color(0.431373, 0.780392, 0.768627, 1) -shader_parameter/ribbon_width = 0.85 -shader_parameter/land_inset = 0.1 +shader_parameter/lead_color = Color(0.78, 0.96, 0.93, 1) +shader_parameter/trail_color = Color(0.5, 0.84, 0.82, 1) +shader_parameter/ribbon_width = 1.35 +shader_parameter/land_inset = 0.12 shader_parameter/tide_speed = 1.42 -shader_parameter/tide_distance = 0.3 -shader_parameter/lead_width = 0.065 -shader_parameter/trail_width = 0.18 -shader_parameter/lead_strength = 0.48 -shader_parameter/trail_strength = 0.25 +shader_parameter/tide_distance = 0.42 +shader_parameter/lead_width = 0.14 +shader_parameter/trail_width = 0.32 +shader_parameter/lead_strength = 0.88 +shader_parameter/trail_strength = 0.52 shader_parameter/phase_scale = 0.035 shader_parameter/phase_strength = 0.55 diff --git a/world/materials/starter_island_grass.tres b/world/materials/starter_island_grass.tres deleted file mode 100644 index 51ea444..0000000 --- a/world/materials/starter_island_grass.tres +++ /dev/null @@ -1,15 +0,0 @@ -[gd_resource type="ShaderMaterial" load_steps=3 format=3] - -[ext_resource type="Texture2D" path="res://art/exported/environment/textures/grass_lite.png" id="1_texture"] -[ext_resource type="Shader" path="res://world/materials/terrain_surface_projection.gdshader" id="2_shader"] - -[resource] -resource_name = "Starter Island Grass" -shader = ExtResource("2_shader") -shader_parameter/albedo_texture = ExtResource("1_texture") -shader_parameter/albedo_tint = Color(1, 1, 1, 1) -shader_parameter/tile_world_size = 2.5 -shader_parameter/blend_sharpness = 6.0 -shader_parameter/top_projection_bias = 2.0 -shader_parameter/roughness = 1.0 -shader_parameter/specular = 0.5 diff --git a/world/materials/starter_island_sand.tres b/world/materials/starter_island_sand.tres deleted file mode 100644 index b967425..0000000 --- a/world/materials/starter_island_sand.tres +++ /dev/null @@ -1,15 +0,0 @@ -[gd_resource type="ShaderMaterial" load_steps=3 format=3] - -[ext_resource type="Texture2D" path="res://art/exported/environment/textures/sand.png" id="1_texture"] -[ext_resource type="Shader" path="res://world/materials/terrain_surface_projection.gdshader" id="2_shader"] - -[resource] -resource_name = "Starter Island Sand" -shader = ExtResource("2_shader") -shader_parameter/albedo_texture = ExtResource("1_texture") -shader_parameter/albedo_tint = Color(1, 1, 1, 1) -shader_parameter/tile_world_size = 2.5 -shader_parameter/blend_sharpness = 6.0 -shader_parameter/top_projection_bias = 2.0 -shader_parameter/roughness = 1.0 -shader_parameter/specular = 0.5 diff --git a/world/materials/stylized_water.gdshader b/world/materials/stylized_water.gdshader index 76bc1a7..fa6c266 100644 --- a/world/materials/stylized_water.gdshader +++ b/world/materials/stylized_water.gdshader @@ -1,5 +1,5 @@ shader_type spatial; -render_mode blend_mix, depth_draw_never, cull_back, unshaded; +render_mode blend_mix, depth_draw_never, cull_back, unshaded, fog_disabled; uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest; @@ -29,15 +29,41 @@ uniform float contour_corner_variation : hint_range(0.0, 0.15, 0.005) = 0.045; uniform float contour_smoothing_pixels : hint_range(0.0, 2.5, 0.05) = 1.0; uniform float contour_smoothing_strength : hint_range(0.0, 1.0, 0.01) = 0.72; uniform float contour_depth_reject : hint_range(0.05, 2.0, 0.01) = 0.60; +uniform float shoreline_horizontal_reach : hint_range(0.05, 1.5, 0.01) = 0.32; +uniform float contact_fade_depth : hint_range(0.0, 0.1, 0.001) = 0.018; uniform float open_water_drift_scale : hint_range(0.001, 0.3, 0.001) = 0.018; uniform vec2 open_water_drift_speed = vec2(0.010, -0.007); uniform float open_water_drift_strength : hint_range(0.0, 0.08, 0.001) = 0.045; +uniform vec4 surface_mark_color : source_color = vec4(0.580, 0.850, 0.860, 1.0); +uniform vec2 surface_mark_density = vec2(1.30, 0.90); +uniform vec2 surface_mark_drift = vec2(0.045, -0.032); +uniform float surface_mark_strength : hint_range(0.0, 0.3, 0.005) = 0.12; +uniform float surface_mottle_scale : hint_range(0.01, 1.0, 0.01) = 0.11; +uniform vec2 surface_mottle_speed = vec2(-0.018, 0.013); +uniform float surface_mottle_strength : hint_range(0.0, 0.2, 0.005) = 0.07; +uniform vec2 shoreline_mark_density = vec2(4.60, 3.20); +uniform vec2 shoreline_mark_drift = vec2(0.085, -0.060); +uniform float shoreline_mark_depth : hint_range(0.1, 2.0, 0.01) = 0.95; +uniform float shoreline_mark_strength : hint_range(0.0, 1.0, 0.01) = 0.82; +uniform float shoreline_base_fill : hint_range(0.0, 1.0, 0.01) = 0.32; +uniform float shoreline_opacity_boost : hint_range(0.0, 0.5, 0.01) = 0.26; +uniform float surface_highlight_night_floor : hint_range(0.0, 1.0, 0.01) = 0.72; +uniform float shoreline_minimum_reach : hint_range(0.1, 1.0, 0.01) = 0.48; uniform float distant_alpha_start : hint_range(10.0, 2000.0, 1.0) = 260.0; uniform float distant_alpha_end : hint_range(20.0, 5000.0, 1.0) = 1400.0; +uniform vec4 environment_tint : source_color = vec4(1.0); +uniform float environment_brightness : hint_range(0.1, 1.2, 0.01) = 1.0; +uniform vec4 weather_fog_color : source_color = vec4(0.025, 0.038, 0.045, 1.0); +uniform float weather_fog_amount : hint_range(0.0, 1.0, 0.01) = 0.0; +uniform float weather_fog_near_amount : hint_range(0.0, 1.0, 0.01) = 0.0; +uniform float weather_fog_begin : hint_range(0.0, 500.0, 0.5) = 20.0; +uniform float weather_fog_end : hint_range(1.0, 1000.0, 0.5) = 95.0; +uniform float weather_fog_curve : hint_range(0.1, 4.0, 0.01) = 1.4; varying vec3 world_position; varying float surface_view_depth; +varying vec3 surface_view_position; float hash_21(vec2 point) { @@ -59,7 +85,63 @@ float value_noise(vec2 point) { } -float reconstruct_view_depth( +float block_mark(vec2 point, vec2 half_size) { + return ( + step(abs(point.x), half_size.x) + * step(abs(point.y), half_size.y) + ); +} + + +float blocky_mark_layer( + vec2 flowing_grid, + float seed, + float occupancy_threshold +) { + float row = floor(flowing_grid.y); + flowing_grid.x += mod(row, 2.0) * 0.43; + vec2 cell = floor(flowing_grid); + vec2 local = fract(flowing_grid) - vec2(0.5); + vec2 jitter = vec2( + hash_21(cell + vec2(3.7 + seed, 9.1)), + hash_21(cell + vec2(7.3, 2.9 + seed)) + ) - vec2(0.5); + local -= jitter * vec2(0.52, 0.46); + local = floor((local + vec2(0.5)) * 8.0) / 8.0 - vec2(0.5); + float shape_key = hash_21(cell + vec2(13.1 + seed, 5.7)); + float size_key = hash_21(cell + vec2(1.9, 17.3 + seed)); + vec2 half_size = mix(vec2(0.10, 0.07), vec2(0.18, 0.12), size_key); + float mark = block_mark(local, half_size); + if (shape_key < 0.34) { + mark = max( + mark, + block_mark( + local - vec2(half_size.x * 0.8, -0.11), + vec2(half_size.x * 0.52, 0.045) + ) + ); + } else if (shape_key < 0.67) { + mark *= 1.0 - block_mark( + local - vec2(half_size.x * 0.7, 0.0), + vec2(0.045) + ); + } else { + mark = max( + mark, + block_mark( + local + vec2(half_size.x * 0.75, 0.10), + vec2(0.055, 0.04) + ) + ); + } + return mark * step( + occupancy_threshold, + hash_21(cell + vec2(19.7 + seed, 23.3)) + ); +} + + +vec3 reconstruct_view_position( vec2 screen_uv, float raw_depth, mat4 inverse_projection @@ -70,22 +152,39 @@ float reconstruct_view_depth( vec3 ndc = vec3(screen_uv * 2.0 - 1.0, raw_depth); #endif vec4 view_position = inverse_projection * vec4(ndc, 1.0); - return -view_position.z / max(abs(view_position.w), 0.00001); + return view_position.xyz / max(abs(view_position.w), 0.00001); +} + + +vec3 reconstruct_world_position( + vec2 screen_uv, + float raw_depth, + mat4 inverse_projection, + mat4 inverse_view +) { + vec3 view_position = reconstruct_view_position( + screen_uv, + raw_depth, + inverse_projection + ); + return (inverse_view * vec4(view_position, 1.0)).xyz; } float sample_water_depth( vec2 screen_uv, - float water_surface_depth, - mat4 inverse_projection + float water_surface_height, + mat4 inverse_projection, + mat4 inverse_view ) { float raw_depth = texture(depth_texture, screen_uv).r; - float scene_depth = reconstruct_view_depth( + vec3 scene_world_position = reconstruct_world_position( screen_uv, raw_depth, - inverse_projection + inverse_projection, + inverse_view ); - return scene_depth - water_surface_depth; + return water_surface_height - scene_world_position.y; } @@ -111,17 +210,24 @@ void vertex() { vec4 view_vertex = VIEW_MATRIX * world_vertex; world_position = world_vertex.xyz; surface_view_depth = -view_vertex.z; + // Godot's depth fog measures radial view-space distance. Keep the existing + // forward depth for distant-water presentation, but pass the linear view + // position so weather fog can derive the same per-fragment radial metric as + // opaque world geometry. Calculating length here would interpolate four + // corner distances across the oversized ocean plane and over-fog its center. + surface_view_position = view_vertex.xyz; } void fragment() { float raw_depth = texture(depth_texture, SCREEN_UV).r; - float scene_view_depth = reconstruct_view_depth( + vec3 scene_world_position = reconstruct_world_position( SCREEN_UV, raw_depth, - INV_PROJECTION_MATRIX + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX ); - float water_depth = max(scene_view_depth - surface_view_depth, 0.0); + float water_depth = max(world_position.y - scene_world_position.y, 0.0); water_depth = min(water_depth, deep_depth * 4.0); float contour_depth = water_depth; @@ -135,23 +241,51 @@ void fragment() { float contour_weight_sum = 1.0; float left_depth = sample_water_depth( SCREEN_UV - vec2(contour_texel.x, 0.0), - surface_view_depth, - INV_PROJECTION_MATRIX + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX ); float right_depth = sample_water_depth( SCREEN_UV + vec2(contour_texel.x, 0.0), - surface_view_depth, - INV_PROJECTION_MATRIX + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX ); float upper_depth = sample_water_depth( SCREEN_UV - vec2(0.0, contour_texel.y), - surface_view_depth, - INV_PROJECTION_MATRIX + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX ); float lower_depth = sample_water_depth( SCREEN_UV + vec2(0.0, contour_texel.y), - surface_view_depth, - INV_PROJECTION_MATRIX + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX + ); + float upper_left_depth = sample_water_depth( + SCREEN_UV - contour_texel, + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX + ); + float upper_right_depth = sample_water_depth( + SCREEN_UV + vec2(contour_texel.x, -contour_texel.y), + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX + ); + float lower_left_depth = sample_water_depth( + SCREEN_UV + vec2(-contour_texel.x, contour_texel.y), + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX + ); + float lower_right_depth = sample_water_depth( + SCREEN_UV + contour_texel, + world_position.y, + INV_PROJECTION_MATRIX, + INV_VIEW_MATRIX ); float left_weight = contour_neighbor_weight( water_depth, left_depth, contour_depth_reject @@ -165,11 +299,37 @@ void fragment() { float lower_weight = contour_neighbor_weight( water_depth, lower_depth, contour_depth_reject ); + float diagonal_scale = 0.70710678; + float upper_left_weight = contour_neighbor_weight( + water_depth, upper_left_depth, contour_depth_reject + ) * diagonal_scale; + float upper_right_weight = contour_neighbor_weight( + water_depth, upper_right_depth, contour_depth_reject + ) * diagonal_scale; + float lower_left_weight = contour_neighbor_weight( + water_depth, lower_left_depth, contour_depth_reject + ) * diagonal_scale; + float lower_right_weight = contour_neighbor_weight( + water_depth, lower_right_depth, contour_depth_reject + ) * diagonal_scale; contour_depth_sum += left_depth * left_weight; contour_depth_sum += right_depth * right_weight; contour_depth_sum += upper_depth * upper_weight; contour_depth_sum += lower_depth * lower_weight; - float neighbor_support = left_weight + right_weight + upper_weight + lower_weight; + contour_depth_sum += upper_left_depth * upper_left_weight; + contour_depth_sum += upper_right_depth * upper_right_weight; + contour_depth_sum += lower_left_depth * lower_left_weight; + contour_depth_sum += lower_right_depth * lower_right_weight; + float neighbor_support = ( + left_weight + + right_weight + + upper_weight + + lower_weight + + upper_left_weight + + upper_right_weight + + lower_left_weight + + lower_right_weight + ); contour_weight_sum += neighbor_support; float smoothed_contour_depth = contour_depth_sum / contour_weight_sum; contour_depth = mix( @@ -180,6 +340,35 @@ void fragment() { contour_feature_support = smoothstep(0.75, 1.75, neighbor_support); } + vec2 scene_world_dx = dFdx(scene_world_position.xz); + vec2 scene_world_dy = dFdy(scene_world_position.xz); + float horizontal_dx = max(length(scene_world_dx), 0.0001); + float horizontal_dy = max(length(scene_world_dy), 0.0001); + float depth_gradient = length(vec2( + dFdx(water_depth) / horizontal_dx, + dFdy(water_depth) / horizontal_dy + )); + float estimated_shore_distance = min( + water_depth / max(depth_gradient, 0.001), + shoreline_horizontal_reach * 4.0 + ); + float proximity_center = shoreline_horizontal_reach * 0.86; + float proximity_filter_width = max( + fwidth(estimated_shore_distance) * 1.5, + shoreline_horizontal_reach * 0.12 + ); + float shoreline_proximity = 1.0 - smoothstep( + max(proximity_center - proximity_filter_width, 0.0), + proximity_center + proximity_filter_width, + estimated_shore_distance + ); + float contact_filter_width = max(fwidth(water_depth) * 1.5, 0.004); + float contact_stability = smoothstep( + max(contact_fade_depth - contact_filter_width, 0.0), + contact_fade_depth + contact_filter_width, + water_depth + ); + float shallow_mix = smoothstep(0.0, shallow_depth, water_depth); float deep_mix = smoothstep(shallow_depth, deep_depth, water_depth); vec3 water_color = mix(shore_color.rgb, shallow_color.rgb, shallow_mix); @@ -205,6 +394,7 @@ void fragment() { contour_depth ); tide_wash *= contour_feature_support; + tide_wash *= shoreline_proximity; tide_wash *= 1.0 - smoothstep(shallow_depth, deep_depth, water_depth); water_color = mix( water_color, @@ -218,6 +408,126 @@ void fragment() { ); } + vec2 flowing_grid = ( + world_position.xz * surface_mark_density + + TIME * surface_mark_drift + ); + float surface_row = floor(flowing_grid.y); + flowing_grid.x += mod(surface_row, 2.0) * 0.43; + vec2 surface_cell = floor(flowing_grid); + vec2 surface_local = fract(flowing_grid) - vec2(0.5); + vec2 surface_jitter = vec2( + hash_21(surface_cell + vec2(3.7, 9.1)), + hash_21(surface_cell + vec2(7.3, 2.9)) + ) - vec2(0.5); + surface_local -= surface_jitter * vec2(0.52, 0.46); + surface_local = ( + floor((surface_local + vec2(0.5)) * 8.0) / 8.0 + - vec2(0.5) + ); + float surface_shape = hash_21(surface_cell + vec2(13.1, 5.7)); + float surface_size = hash_21(surface_cell + vec2(1.9, 17.3)); + vec2 surface_half_size = mix( + vec2(0.10, 0.07), + vec2(0.18, 0.12), + surface_size + ); + float surface_mark = block_mark(surface_local, surface_half_size); + if (surface_shape < 0.34) { + surface_mark = max( + surface_mark, + block_mark( + surface_local - vec2(surface_half_size.x * 0.8, -0.11), + vec2(surface_half_size.x * 0.52, 0.045) + ) + ); + } else if (surface_shape < 0.67) { + surface_mark *= 1.0 - block_mark( + surface_local - vec2(surface_half_size.x * 0.7, 0.0), + vec2(0.045) + ); + } else { + surface_mark = max( + surface_mark, + block_mark( + surface_local + vec2(surface_half_size.x * 0.75, 0.10), + vec2(0.055, 0.04) + ) + ); + } + surface_mark *= step( + 0.22, + hash_21(surface_cell + vec2(19.7, 23.3)) + ); + float mark_visibility = mix(1.0, 0.68, deep_mix); + float surface_highlight = ( + surface_mark * surface_mark_strength * mark_visibility + ); + + vec2 shoreline_phase_coordinates = world_position.xz * phase_noise_scale; + float shoreline_phase = ( + value_noise(shoreline_phase_coordinates) + + value_noise( + shoreline_phase_coordinates * 0.47 + vec2(9.7, 3.1) + ) * 0.5 + - 0.75 + ) * phase_noise_strength; + float shoreline_cycle = ( + sin(TIME * tide_speed + shoreline_phase) * 0.5 + 0.5 + ); + float animated_shoreline_depth = shoreline_mark_depth * mix( + shoreline_minimum_reach, + 1.0, + shoreline_cycle + ); + float shoreline_mask = 1.0 - smoothstep( + max(animated_shoreline_depth * 0.08, 0.01), + max(animated_shoreline_depth, 0.02), + contour_depth + ); + float shoreline_depth_visibility = 1.0 - smoothstep( + shallow_depth, + deep_depth, + water_depth + ); + shoreline_mask *= shoreline_depth_visibility; + float shoreline_marks = max( + blocky_mark_layer( + world_position.xz * shoreline_mark_density + + TIME * shoreline_mark_drift, + 17.0, + 0.04 + ), + blocky_mark_layer( + world_position.xz * shoreline_mark_density * 1.37 + - TIME * shoreline_mark_drift.yx * 0.73, + 41.0, + 0.08 + ) + ); + float shoreline_foam = shoreline_mask * mix( + shoreline_base_fill, + 1.0, + shoreline_marks + ); + shoreline_foam *= shoreline_proximity; + water_alpha = min( + water_alpha + shoreline_foam * shoreline_opacity_boost, + 0.98 + ); + // Preserve most of the water layer at exact terrain contact. Fully fading + // it exposed the sand texture as a broken tan seam along the water plane. + water_alpha *= mix(0.82, 1.0, contact_stability); + + float surface_mottle = value_noise( + world_position.xz * surface_mottle_scale + + TIME * surface_mottle_speed + ); + surface_mottle = floor(surface_mottle * 6.0) / 5.0; + water_color *= 1.0 + ( + surface_mottle * 2.0 - 1.0 + ) * surface_mottle_strength; + vec2 drift_coordinates = ( world_position.xz * open_water_drift_scale + TIME * open_water_drift_speed @@ -233,8 +543,49 @@ void fragment() { water_color = mix(water_color, deep_color.rgb, distant_mix * 0.72); water_alpha = mix(water_alpha, 0.96, distant_mix); + water_color *= environment_tint.rgb * environment_brightness; + vec3 visible_highlight_color = surface_mark_color.rgb * max( + environment_tint.rgb * environment_brightness, + vec3(surface_highlight_night_floor) + ); + float visible_highlight = max( + surface_highlight, + shoreline_foam * shoreline_mark_strength + ); + water_color = mix( + water_color, + visible_highlight_color, + clamp(visible_highlight, 0.0, 1.0) + ); + float surface_view_distance = length(surface_view_position); + float weather_fog_distance = smoothstep( + weather_fog_begin, + max(weather_fog_end, weather_fog_begin + 1.0), + surface_view_distance + ); + weather_fog_distance = pow( + clamp(weather_fog_distance, 0.0, 1.0), + weather_fog_curve + ); + float distance_fog_mix = weather_fog_amount * weather_fog_distance; + float weather_fog_mix = 1.0 - ( + (1.0 - weather_fog_near_amount) + * (1.0 - distance_fog_mix) + ); + water_color = mix( + water_color, + weather_fog_color.rgb, + weather_fog_mix + ); + float weather_fog_alpha = mix( + 0.96, + 1.0, + weather_fog_near_amount + ); + water_alpha = mix(water_alpha, weather_fog_alpha, weather_fog_mix); + ALBEDO = water_color; - ALPHA = clamp(water_alpha, 0.1, 0.96); + ALPHA = clamp(water_alpha, 0.1, weather_fog_alpha); METALLIC = 0.0; SPECULAR = 0.0; ROUGHNESS = 1.0; diff --git a/world/materials/stylized_water.tres b/world/materials/stylized_water.tres index 40fc17d..7f4671a 100644 --- a/world/materials/stylized_water.tres +++ b/world/materials/stylized_water.tres @@ -1,6 +1,6 @@ -[gd_resource type="ShaderMaterial" load_steps=2 format=3] +[gd_resource type="ShaderMaterial" format=3 uid="uid://6lafg0ac2qul"] -[ext_resource type="Shader" path="res://world/materials/stylized_water.gdshader" id="1_water_shader"] +[ext_resource type="Shader" uid="uid://lvo7xyr3c1k3" path="res://world/materials/stylized_water.gdshader" id="1_water_shader"] [resource] render_priority = 0 @@ -11,24 +11,43 @@ shader_parameter/deep_color = Color(0.109804, 0.345098, 0.435294, 1) shader_parameter/tide_wash_color = Color(0.360784, 0.721569, 0.733333, 1) shader_parameter/shallow_depth = 0.7 shader_parameter/deep_depth = 3.6 -shader_parameter/shore_depth_width = 0.16 -shader_parameter/shore_alpha = 0.44 -shader_parameter/shallow_alpha = 0.6 -shader_parameter/deep_alpha = 0.82 +shader_parameter/shore_depth_width = 0.1 +shader_parameter/shore_alpha = 0.68 +shader_parameter/shallow_alpha = 0.78 +shader_parameter/deep_alpha = 0.9 shader_parameter/tide_effect_enabled = true shader_parameter/tide_speed = 1.42 -shader_parameter/tide_distance = 0.3 -shader_parameter/tide_wash_width = 0.38 +shader_parameter/tide_distance = 0.11 +shader_parameter/tide_wash_width = 0.16 shader_parameter/tide_wash_strength = 0.11 shader_parameter/tide_wash_alpha_shift = 0.045 shader_parameter/phase_noise_scale = 0.055 shader_parameter/phase_noise_strength = 0.68 -shader_parameter/contour_corner_variation = 0.045 -shader_parameter/contour_smoothing_pixels = 1.0 -shader_parameter/contour_smoothing_strength = 0.72 +shader_parameter/contour_corner_variation = 0.025 +shader_parameter/contour_smoothing_pixels = 1.35 +shader_parameter/contour_smoothing_strength = 0.88 shader_parameter/contour_depth_reject = 0.6 +shader_parameter/shoreline_horizontal_reach = 0.32 +shader_parameter/contact_fade_depth = 0.018 shader_parameter/open_water_drift_scale = 0.018 shader_parameter/open_water_drift_speed = Vector2(0.01, -0.007) shader_parameter/open_water_drift_strength = 0.045 +shader_parameter/surface_mark_color = Color(0.58, 0.85, 0.86, 1) +shader_parameter/surface_mark_density = Vector2(1.5, 1.05) +shader_parameter/surface_mark_drift = Vector2(0.045, -0.032) +shader_parameter/surface_mark_strength = 0.12 +shader_parameter/surface_mottle_scale = 0.11 +shader_parameter/surface_mottle_speed = Vector2(-0.018, 0.013) +shader_parameter/surface_mottle_strength = 0.07 +shader_parameter/shoreline_mark_density = Vector2(4.6, 3.2) +shader_parameter/shoreline_mark_drift = Vector2(0.085, -0.06) +shader_parameter/shoreline_mark_depth = 0.55 +shader_parameter/shoreline_mark_strength = 0.95 +shader_parameter/shoreline_base_fill = 0.58 +shader_parameter/shoreline_opacity_boost = 0.38 +shader_parameter/surface_highlight_night_floor = 0.72 +shader_parameter/shoreline_minimum_reach = 0.22 shader_parameter/distant_alpha_start = 260.0 shader_parameter/distant_alpha_end = 1400.0 +shader_parameter/environment_tint = Color(1, 1, 1, 1) +shader_parameter/environment_brightness = 1.0 diff --git a/world/materials/stylized_water_fresh.tres b/world/materials/stylized_water_fresh.tres index 66cc373..5fe4c8f 100644 --- a/world/materials/stylized_water_fresh.tres +++ b/world/materials/stylized_water_fresh.tres @@ -1,6 +1,6 @@ -[gd_resource type="ShaderMaterial" load_steps=2 format=3] +[gd_resource type="ShaderMaterial" format=3 uid="uid://d27er7sx5bcqw"] -[ext_resource type="Shader" path="res://world/materials/stylized_water.gdshader" id="1_water_shader"] +[ext_resource type="Shader" uid="uid://lvo7xyr3c1k3" path="res://world/materials/stylized_water.gdshader" id="1_water_shader"] [resource] render_priority = 0 @@ -11,13 +11,43 @@ shader_parameter/deep_color = Color(0.109804, 0.345098, 0.435294, 1) shader_parameter/tide_wash_color = Color(0.360784, 0.721569, 0.733333, 1) shader_parameter/shallow_depth = 0.7 shader_parameter/deep_depth = 3.6 -shader_parameter/shore_depth_width = 0.16 -shader_parameter/shore_alpha = 0.44 -shader_parameter/shallow_alpha = 0.6 -shader_parameter/deep_alpha = 0.82 +shader_parameter/shore_depth_width = 0.1 +shader_parameter/shore_alpha = 0.68 +shader_parameter/shallow_alpha = 0.78 +shader_parameter/deep_alpha = 0.9 shader_parameter/tide_effect_enabled = false +shader_parameter/tide_speed = 1.42 +shader_parameter/tide_distance = 0.3 +shader_parameter/tide_wash_width = 0.38 +shader_parameter/tide_wash_strength = 0.11 +shader_parameter/tide_wash_alpha_shift = 0.045 +shader_parameter/phase_noise_scale = 0.055 +shader_parameter/phase_noise_strength = 0.68 +shader_parameter/contour_corner_variation = 0.045 +shader_parameter/contour_smoothing_pixels = 1.35 +shader_parameter/contour_smoothing_strength = 0.88 +shader_parameter/contour_depth_reject = 0.6 +shader_parameter/shoreline_horizontal_reach = 0.32 +shader_parameter/contact_fade_depth = 0.018 shader_parameter/open_water_drift_scale = 0.018 shader_parameter/open_water_drift_speed = Vector2(0.01, -0.007) shader_parameter/open_water_drift_strength = 0.045 +shader_parameter/surface_mark_color = Color(0.58, 0.85, 0.86, 1) +shader_parameter/surface_mark_density = Vector2(1.5, 1.05) +shader_parameter/surface_mark_drift = Vector2(0.045, -0.032) +shader_parameter/surface_mark_strength = 0.12 +shader_parameter/surface_mottle_scale = 0.11 +shader_parameter/surface_mottle_speed = Vector2(-0.018, 0.013) +shader_parameter/surface_mottle_strength = 0.07 +shader_parameter/shoreline_mark_density = Vector2(4.6, 3.2) +shader_parameter/shoreline_mark_drift = Vector2(0.085, -0.06) +shader_parameter/shoreline_mark_depth = 0.55 +shader_parameter/shoreline_mark_strength = 0.95 +shader_parameter/shoreline_base_fill = 0.58 +shader_parameter/shoreline_opacity_boost = 0.38 +shader_parameter/surface_highlight_night_floor = 0.72 +shader_parameter/shoreline_minimum_reach = 0.22 shader_parameter/distant_alpha_start = 260.0 shader_parameter/distant_alpha_end = 1400.0 +shader_parameter/environment_tint = Color(1, 1, 1, 1) +shader_parameter/environment_brightness = 1.0 diff --git a/world/materials/terrain_surface_projection.gdshader b/world/materials/terrain_surface_projection.gdshader index 4842e50..b879c63 100644 --- a/world/materials/terrain_surface_projection.gdshader +++ b/world/materials/terrain_surface_projection.gdshader @@ -1,7 +1,7 @@ shader_type spatial; render_mode cull_back; -uniform sampler2D albedo_texture : source_color, repeat_enable, filter_nearest_mipmap_anisotropic; +uniform sampler2D albedo_texture : source_color, repeat_enable, filter_nearest; uniform vec4 albedo_tint : source_color = vec4(1.0); uniform float tile_world_size : hint_range(0.1, 20.0, 0.05) = 2.5; uniform float blend_sharpness : hint_range(1.0, 16.0, 0.25) = 6.0; diff --git a/world/rain_ambience.gd b/world/rain_ambience.gd new file mode 100644 index 0000000..acea9fd --- /dev/null +++ b/world/rain_ambience.gd @@ -0,0 +1,121 @@ +class_name RainAmbience +extends AudioStreamPlayer + +const RAIN_STREAM_PATH: String = ( + "res://audio/ambience/rain_loop_ontario.ogg" +) +const SILENT_VOLUME_DB: float = -60.0 + +@export_range(-40.0, 0.0, 0.5) var rain_volume_db: float = -8.0 +@export_range(0.0, 10.0, 0.1) var fade_seconds: float = 2.5 + +var _weather_service: WorldWeatherService +var _fade_tween: Tween +var _is_active: bool = false + + +func _ready() -> void: + bus = &"Environment" + var rain_stream := load(RAIN_STREAM_PATH) as AudioStreamOggVorbis + var runtime_stream := ( + rain_stream.duplicate() as AudioStreamOggVorbis + if rain_stream != null + else null + ) + if runtime_stream != null: + runtime_stream.loop = true + stream = runtime_stream + volume_db = SILENT_VOLUME_DB + + +func configure(weather_service: WorldWeatherService) -> void: + if ( + _weather_service != null + and _weather_service.weather_changed.is_connected( + _on_weather_changed + ) + ): + _weather_service.weather_changed.disconnect(_on_weather_changed) + _weather_service = weather_service + if ( + _weather_service != null + and not _weather_service.weather_changed.is_connected( + _on_weather_changed + ) + ): + _weather_service.weather_changed.connect(_on_weather_changed) + + +func set_active(active: bool) -> void: + if _is_active == active: + return + _is_active = active + if not active: + _silence_immediately() + return + _transition_to_rain( + _weather_service != null and _weather_service.is_raining() + ) + + +func _on_weather_changed( + weather: WorldWeatherService.Weather, + _seconds_remaining: float, +) -> void: + _transition_to_rain(weather == WorldWeatherService.Weather.RAINY) + + +func _transition_to_rain(is_raining: bool) -> void: + if not _is_active: + return + _stop_fade() + if is_raining: + if stream == null: + return + if not playing: + volume_db = SILENT_VOLUME_DB + play() + _fade_volume_to(rain_volume_db, false) + return + if playing: + _fade_volume_to(SILENT_VOLUME_DB, true) + + +func _fade_volume_to(target_volume_db: float, stop_after_fade: bool) -> void: + if fade_seconds <= 0.0: + volume_db = target_volume_db + if stop_after_fade: + _finish_fade_out() + return + _fade_tween = create_tween() + _fade_tween.set_trans(Tween.TRANS_SINE) + _fade_tween.set_ease(Tween.EASE_IN_OUT) + _fade_tween.tween_property( + self, + "volume_db", + target_volume_db, + fade_seconds, + ) + if stop_after_fade: + _fade_tween.tween_callback(_finish_fade_out) + + +func _finish_fade_out() -> void: + if _weather_service != null and _weather_service.is_raining(): + return + stop() + volume_db = SILENT_VOLUME_DB + _fade_tween = null + + +func _silence_immediately() -> void: + _stop_fade() + stop() + volume_db = SILENT_VOLUME_DB + + +func _stop_fade() -> void: + if _fade_tween == null: + return + _fade_tween.kill() + _fade_tween = null diff --git a/world/rain_ambience.gd.uid b/world/rain_ambience.gd.uid new file mode 100644 index 0000000..0eccbff --- /dev/null +++ b/world/rain_ambience.gd.uid @@ -0,0 +1 @@ +uid://dbpt2uqm82kmn diff --git a/world/regions/starter_island_region.gd b/world/regions/starter_island_region.gd index 399c937..4c8880c 100644 --- a/world/regions/starter_island_region.gd +++ b/world/regions/starter_island_region.gd @@ -5,14 +5,22 @@ extends WorldRegion const FishingShopInteractionType = preload( "res://world/fishing_shop_interaction.gd" ) -const GRASS_SURFACE_NAME: String = "grass_lite" -const SAND_SURFACE_NAME: String = "sand" +const FOLIAGE_WIND_SHADER: Shader = preload( + "res://world/materials/foliage_wind.gdshader" +) +const FOLIAGE_MATERIAL_NAMES: Array[StringName] = [ + &"leaf", + &"leaf_light", + &"leaf_dark", +] @export_group("Owned Nodes") @export_node_path("MeshInstance3D") var visual_mesh_path: NodePath = ( ^"Terrain/Visual/starter_island" ) +@export_node_path("Node3D") +var terrain_visual_root_path: NodePath = ^"Terrain/Visual" @export_node_path("CollisionShape3D") var terrain_collision_shape_path: NodePath = ( ^"Terrain/Collision/Shape" @@ -23,24 +31,18 @@ var player_spawn_path: NodePath = ^"PlayerSpawn" var fishing_shop_path: NodePath = ( ^"Interactables/FishingShopWorld/InteractionArea" ) -@export_node_path("Node3D") -var pelican_landmark_path: NodePath = ( - ^"Interactables/PelicanCoolerPerch" -) - -@export_group("Grass Surface") -@export var grass_material: Material -@export_range(0, 16, 1) var grass_surface_index: int = 0 - -@export_group("Sand Surface") -@export var sand_material: Material -@export_range(0, 16, 1) var sand_surface_index: int = 2 - +@export_group("Terrain Collision") +# The imported GLB is the collision authority. Rebuild once per region load so +# newly authored terrain and props cannot retain a stale saved fallback shape. +@export var rebuild_terrain_collision_on_ready: bool = true +@export_group("Foliage Wind") +@export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.36 +@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9 func _ready() -> void: - _apply_grass_material() - _apply_sand_material() - _build_terrain_collision() + if rebuild_terrain_collision_on_ready or not has_terrain_collision(): + _build_terrain_collision() + _apply_foliage_wind() if Engine.is_editor_hint(): update_configuration_warnings() @@ -54,8 +56,8 @@ func get_fishing_shop() -> FishingShopInteractionType: return get_node_or_null(fishing_shop_path) as FishingShopInteractionType -func get_pelican_landmark() -> Node3D: - return get_node_or_null(pelican_landmark_path) as Node3D +func get_saltwater_shoreline_mesh() -> MeshInstance3D: + return get_node_or_null(^"ShorelineRibbons/Ocean") as MeshInstance3D func has_terrain_collision() -> bool: @@ -65,108 +67,143 @@ func has_terrain_collision() -> bool: return collision_shape != null and collision_shape.shape != null -func _apply_grass_material() -> void: - var visual_mesh: MeshInstance3D = ( - get_node_or_null(visual_mesh_path) as MeshInstance3D - ) - if visual_mesh == null or visual_mesh.mesh == null: - push_error("Starter island visual mesh is unavailable.") - return - if grass_material == null: - push_error("Starter island grass material is unavailable.") - return - if grass_surface_index >= visual_mesh.mesh.get_surface_count(): - push_error("Starter island grass surface index is invalid.") - return - if ( - visual_mesh.mesh.surface_get_name(grass_surface_index) - != GRASS_SURFACE_NAME - ): - push_error("Starter island grass surface name does not match.") - return - visual_mesh.set_surface_override_material( - grass_surface_index, - grass_material - ) - - -func _apply_sand_material() -> void: - var visual_mesh: MeshInstance3D = ( - get_node_or_null(visual_mesh_path) as MeshInstance3D - ) - if visual_mesh == null or visual_mesh.mesh == null: - push_error("Starter island visual mesh is unavailable.") - return - if sand_material == null: - push_error("Starter island sand material is unavailable.") - return - if sand_surface_index >= visual_mesh.mesh.get_surface_count(): - push_error("Starter island sand surface index is invalid.") - return - if ( - visual_mesh.mesh.surface_get_name(sand_surface_index) - != SAND_SURFACE_NAME - ): - push_error("Starter island sand surface name does not match.") - return - visual_mesh.set_surface_override_material( - sand_surface_index, - sand_material - ) - - func _build_terrain_collision() -> void: - var visual_mesh: MeshInstance3D = ( - get_node_or_null(visual_mesh_path) as MeshInstance3D - ) + var terrain_root: Node = get_node_or_null(terrain_visual_root_path) + if terrain_root == null: + terrain_root = get_node_or_null(visual_mesh_path) var collision_shape: CollisionShape3D = ( get_node_or_null(terrain_collision_shape_path) as CollisionShape3D ) - if visual_mesh == null or visual_mesh.mesh == null: - push_error("Starter island visual mesh is unavailable.") + if terrain_root == null: + push_error("Starter island visual terrain root is unavailable.") return if collision_shape == null: push_error("Starter island collision owner is unavailable.") return - var terrain_shape: ConcavePolygonShape3D = ( - visual_mesh.mesh.create_trimesh_shape() - ) - if terrain_shape == null or terrain_shape.get_faces().is_empty(): + var mesh_instances := _collect_terrain_mesh_instances(terrain_root) + if mesh_instances.is_empty(): + var visual_mesh: MeshInstance3D = terrain_root as MeshInstance3D + if visual_mesh == null or visual_mesh.mesh == null: + push_error("Starter island visual mesh is unavailable.") + return + var fallback_shape: ConcavePolygonShape3D = ( + visual_mesh.mesh.create_trimesh_shape() + ) + if fallback_shape == null or fallback_shape.get_faces().is_empty(): + push_error("Starter island terrain collision could not be created.") + return + collision_shape.shape = fallback_shape + return + var terrain_shape := ConcavePolygonShape3D.new() + var faces := PackedVector3Array() + for mesh_instance: MeshInstance3D in mesh_instances: + var mesh: Mesh = mesh_instance.mesh + if mesh == null: + continue + var mesh_shape := mesh.create_trimesh_shape() + if mesh_shape == null: + continue + for face_vertex: Vector3 in mesh_shape.get_faces(): + faces.append(to_local(mesh_instance.to_global(face_vertex))) + if faces.is_empty(): push_error("Starter island terrain collision could not be created.") return + terrain_shape.set_faces(faces) collision_shape.shape = terrain_shape +func _collect_terrain_mesh_instances(root: Node) -> Array[MeshInstance3D]: + var mesh_instances: Array[MeshInstance3D] = [] + if root is MeshInstance3D: + mesh_instances.append(root as MeshInstance3D) + for child: Node in root.get_children(): + mesh_instances.append_array(_collect_terrain_mesh_instances(child)) + return mesh_instances + + +func _apply_foliage_wind() -> void: + var terrain_root: Node = get_node_or_null(terrain_visual_root_path) + if terrain_root == null: + return + for mesh_instance: MeshInstance3D in _collect_terrain_mesh_instances( + terrain_root + ): + _apply_foliage_wind_to_mesh(mesh_instance) + + +func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void: + if mesh_instance.mesh == null: + return + var bounds: AABB = mesh_instance.get_aabb() + var mesh_global_scale: Vector3 = mesh_instance.global_basis.get_scale().abs() + var horizontal_scale: float = maxf( + (mesh_global_scale.x + mesh_global_scale.z) * 0.5, + 0.001, + ) + var local_strength: float = foliage_wind_strength / horizontal_scale + var phase: float = fposmod( + mesh_instance.global_position.x * 0.73 + + mesh_instance.global_position.z * 0.41, + TAU, + ) + for surface_index: int in mesh_instance.mesh.get_surface_count(): + var source_material: Material = mesh_instance.get_active_material( + surface_index + ) + if not source_material is StandardMaterial3D: + continue + if not FOLIAGE_MATERIAL_NAMES.has( + StringName(source_material.resource_name) + ): + continue + var source_standard := source_material as StandardMaterial3D + var wind_material := ShaderMaterial.new() + wind_material.shader = FOLIAGE_WIND_SHADER + wind_material.set_shader_parameter( + "albedo_color", + source_standard.albedo_color, + ) + wind_material.set_shader_parameter( + "material_roughness", + source_standard.roughness, + ) + wind_material.set_shader_parameter( + "material_metallic", + source_standard.metallic, + ) + wind_material.set_shader_parameter("local_min_y", bounds.position.y) + wind_material.set_shader_parameter( + "local_height", + maxf(bounds.size.y, 0.001), + ) + wind_material.set_shader_parameter( + "local_wind_strength", + local_strength, + ) + wind_material.set_shader_parameter("wind_speed", foliage_wind_speed) + wind_material.set_shader_parameter("wind_phase", phase) + mesh_instance.set_surface_override_material( + surface_index, + wind_material, + ) + + func _get_configuration_warnings() -> PackedStringArray: var warnings := PackedStringArray() var visual_mesh := get_node_or_null(visual_mesh_path) as MeshInstance3D + if visual_mesh == null: + warnings.append("Terrain/Visual terrain mesh node is unavailable.") + return warnings if visual_mesh == null or visual_mesh.mesh == null: warnings.append("Terrain/Visual must provide the island mesh.") - elif grass_surface_index >= visual_mesh.mesh.get_surface_count(): - warnings.append("Grass surface index is outside the terrain mesh.") - elif ( - visual_mesh.mesh.surface_get_name(grass_surface_index) - != GRASS_SURFACE_NAME - ): - warnings.append("Grass surface index must identify the grass surface.") - if grass_material == null: - warnings.append("Assign the starter-island grass material.") - if visual_mesh != null and visual_mesh.mesh != null: - if sand_surface_index >= visual_mesh.mesh.get_surface_count(): - warnings.append("Sand surface index is outside the terrain mesh.") - elif ( - visual_mesh.mesh.surface_get_name(sand_surface_index) - != SAND_SURFACE_NAME - ): - warnings.append("Sand surface index must identify the sand surface.") - if sand_material == null: - warnings.append("Assign the starter-island sand material.") + return warnings + if get_node_or_null(terrain_collision_shape_path) == null: warnings.append("Terrain/Collision must provide a collision shape.") + if get_node_or_null(terrain_visual_root_path) == null: + warnings.append("Terrain/Visual root is missing for terrain collision generation.") if get_node_or_null(player_spawn_path) == null: warnings.append("PlayerSpawn marker is missing.") if get_node_or_null(fishing_shop_path) == null: warnings.append("Fishing Shop placement is missing.") - if get_node_or_null(pelican_landmark_path) == null: - warnings.append("Pelican placement is missing.") return warnings diff --git a/world/regions/starter_island_region.tscn b/world/regions/starter_island_region.tscn index 8345704..d956feb 100644 --- a/world/regions/starter_island_region.tscn +++ b/world/regions/starter_island_region.tscn @@ -8,16 +8,13 @@ [ext_resource type="Script" uid="uid://dnlbnlimgxoc2" path="res://world/player_water_trigger.gd" id="5_trigger"] [ext_resource type="Script" uid="uid://df2lsduguegxh" path="res://world/safe_respawn_point.gd" id="6_safe"] [ext_resource type="PackedScene" path="res://world/interactables/fishing_shop_world.tscn" id="7_shop"] -[ext_resource type="PackedScene" path="res://world/interactables/pelican_buyer_world.tscn" id="8_pelican"] -[ext_resource type="Material" path="res://world/materials/starter_island_grass.tres" id="9_grass"] [ext_resource type="Script" uid="uid://dtcgnxl1kmdu1" path="res://world/water_body_authoring.gd" id="10_water_body"] -[ext_resource type="Material" path="res://world/materials/starter_island_sand.tres" id="11_sand"] [ext_resource type="Material" path="res://world/materials/stylized_water.tres" id="13_water"] -[ext_resource type="Script" path="res://world/water/shoreline_ribbon_baker.gd" id="14_shoreline_baker"] -[ext_resource type="Script" path="res://world/water/shoreline_ribbon_config.gd" id="15_shoreline_config"] [ext_resource type="ArrayMesh" path="res://world/generated/shorelines/starter_ocean_shoreline.tres" id="17_ocean_shoreline"] -[ext_resource type="Material" path="res://world/materials/shoreline_tide.tres" id="18_shoreline_material"] [ext_resource type="Material" path="res://world/materials/stylized_water_fresh.tres" id="19_fresh_water"] +[ext_resource type="Script" path="res://world/water_surface_motion.gd" id="20_surface_motion"] +[ext_resource type="Script" path="res://world/water/shoreline_ribbon_baker.gd" id="21_shoreline_baker"] +[ext_resource type="Script" path="res://world/water/shoreline_ribbon_config.gd" id="22_shoreline_config"] [sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_4e4k5"] data = PackedVector3Array(10.033899, 3.3913999, 3.3446, 13.3786, 3.3913999, 0, 13.3786, 3.3913999, 3.3446, 10.033899, 3.3913999, 3.3446, 10.033899, 3.3913999, 0, 13.3786, 3.3913999, 0, 6.6893, 3.3913999, 3.3446, 10.033899, 3.3913999, 0, 10.033899, 3.3913999, 3.3446, 6.6893, 3.3913999, 3.3446, 6.6893, 3.3913999, 0, 10.033899, 3.3913999, 0, 6.6893, 3.3913999, 0, 10.033899, 3.3913999, -3.3446, 10.033899, 3.3913999, 0, 6.6893, 3.3913999, 6.6893, 6.6893, 3.3913999, 3.3446, 10.033899, 3.3913999, 3.3446, 6.6893, 3.3913999, 6.6893, 10.033899, 3.3913999, 3.3446, 10.033899, 3.3913999, 6.6893, 3.3446, 3.3913999, 3.3446, 6.6893, 3.3913999, 0, 6.6893, 3.3913999, 3.3446, 6.6893, 3.3913999, 10.033899, 6.6893, 3.3913999, 6.6893, 10.033899, 3.3913999, 6.6893, 6.6893, 3.3913999, 10.033899, 10.033899, 3.3913999, 6.6893, 10.033899, 3.3913999, 10.033899, 3.3446, 3.3913999, 6.6893, 6.6893, 3.3913999, 3.3446, 6.6893, 3.3913999, 6.6893, 3.3446, 3.3913999, 6.6893, 3.3446, 3.3913999, 3.3446, 6.6893, 3.3913999, 3.3446, 3.3446, 3.3913999, 10.033899, 6.6893, 3.3913999, 6.6893, 6.6893, 3.3913999, 10.033899, 3.3446, 3.3913999, 10.033899, 3.3446, 3.3913999, 6.6893, 6.6893, 3.3913999, 6.6893, 0, 3.3913999, 6.6893, 3.3446, 3.3913999, 3.3446, 3.3446, 3.3913999, 6.6893, 0, 3.3913999, 6.6893, 0, 3.3913999, 3.3446, 3.3446, 3.3913999, 3.3446, 0, 3.3913999, 10.033899, 0, 3.3913999, 6.6893, 3.3446, 3.3913999, 6.6893, 0, 3.3913999, 10.033899, 3.3446, 3.3913999, 6.6893, 3.3446, 3.3913999, 10.033899, -3.3446, 3.3913999, 13.3786, -3.3446, 3.3913999, 10.033899, 0, 3.3913999, 10.033899, -3.3446, 3.3913999, 13.3786, 0, 3.3913999, 10.033899, 0, 3.3913999, 13.3786, 0, 3.3913999, 13.3786, 0, 3.3913999, 10.033899, 3.3446, 3.3913999, 10.033899, 0, 3.3913999, 13.3786, 3.3446, 3.3913999, 10.033899, 3.3446, 3.3913999, 13.3786, 3.3446, 3.3913999, 13.3786, 3.3446, 3.3913999, 10.033899, 6.6893, 3.3913999, 10.033899, 3.3446, 3.3913999, 13.3786, 6.6893, 3.3913999, 10.033899, 6.6893, 3.3913999, 13.3786, 3.9148, 3.3913999, 16.514599, 3.3446, 3.3913999, 13.3786, 6.6893, 3.3913999, 13.3786, 3.3446, 3.3913999, 13.3786, 3.9148, 3.3913999, 16.514599, 3.6399, 3.3913999, 16.2405, 3.9148, 3.3913999, 16.514599, 6.6893, 3.3913999, 13.3786, 6.6893, 3.3913999, 16.7232, 6.6893, 3.3913999, 16.7232, 6.6949, 3.3913999, 18.9914, 6.6893, 3.3913999, 18.9861, 6.6949, 3.3913999, 18.9914, 6.6893, 3.3913999, 16.7232, 10.3743, 3.3913999, 16.7232, 6.6949, 3.3913999, 18.9914, 10.3743, 3.3913999, 16.7232, 9.1275, 3.3913999, 18.830599, 10.033899, 3.3913999, -10.033899, 13.3786, 3.3913999, -13.3786, 13.3786, 3.3913999, -10.033899, 13.3786, 3.3913999, -13.3786, 16.7232, 3.3913999, -10.033899, 13.3786, 3.3913999, -10.033899, 16.7232, 3.3913999, -10.033899, 19.798899, 3.3913999, -13.1096, 19.798899, 3.3913999, -10.033899, 16.7232, 3.3913999, -10.033899, 16.7232, 3.3913999, -13.1096, 19.798899, 3.3913999, -13.1096, 16.7232, 3.3913999, -10.033899, 16.4543, 3.3913999, -13.3786, 16.7232, 3.3913999, -13.1096, 16.7232, 3.3913999, -10.033899, 13.3786, 3.3913999, -13.3786, 16.4543, 3.3913999, -13.3786, 13.3786, 3.3913999, -6.6893, 13.3786, 3.3913999, -10.033899, 16.7232, 3.3913999, -10.033899, 13.3786, 3.3913999, -6.6893, 16.7232, 3.3913999, -10.033899, 16.7232, 3.3913999, -6.6893, 10.033899, 3.3913999, -6.6893, 13.3786, 3.3913999, -10.033899, 13.3786, 3.3913999, -6.6893, 10.033899, 3.3913999, -6.6893, 10.033899, 3.3913999, -10.033899, 13.3786, 3.3913999, -10.033899, 16.4543, 3.3913999, -13.3786, 16.495699, 3.3913999, -16.459, 16.495699, 3.3913999, -13.42, 16.4543, 3.3913999, -13.3786, 12.738999, 3.3913999, -15.894899, 16.495699, 3.3913999, -16.459, 12.738999, 3.3913999, -15.894899, 16.4543, 3.3913999, -13.3786, 13.3786, 3.3913999, -13.3786, 13.3786, 3.3913999, -13.3786, 12.460899, 3.3913999, -16.1674, 12.738999, 3.3913999, -15.894899, 13.3786, 3.3913999, -13.3786, 10.033899, 3.3913999, -16.7232, 12.460899, 3.3913999, -16.1674, 10.033899, 3.3913999, -10.033899, 10.033899, 3.3913999, -13.3786, 13.3786, 3.3913999, -13.3786, 10.033899, 3.3913999, -16.7232, 13.3786, 3.3913999, -13.3786, 10.033899, 3.3913999, -13.3786, 6.6893, 3.3913999, -13.3786, 10.033899, 3.3913999, -16.7232, 10.033899, 3.3913999, -13.3786, 6.6893, 3.3913999, -13.3786, 6.6893, 3.3913999, -16.7232, 10.033899, 3.3913999, -16.7232, 6.6893, 3.3913999, -10.033899, 10.033899, 3.3913999, -13.3786, 10.033899, 3.3913999, -10.033899, 6.6893, 3.3913999, -10.033899, 6.6893, 3.3913999, -13.3786, 10.033899, 3.3913999, -13.3786, 6.6893, 3.3913999, -6.6893, 6.6893, 3.3913999, -10.033899, 10.033899, 3.3913999, -10.033899, 6.6893, 3.3913999, -6.6893, 10.033899, 3.3913999, -10.033899, 10.033899, 3.3913999, -6.6893, 4.0225, 3.3913999, -8.7143, 6.6893, 3.3913999, -13.3786, 6.6893, 3.3913999, -10.033899, 4.6821, 3.3913999, -13.3786, 6.6893, 3.3913999, -16.7232, 6.6893, 3.3913999, -13.3786, 4.0225, 3.3913999, -8.7143, 4.6821, 3.3913999, -13.3786, 6.6893, 3.3913999, -13.3786, 4.6821, 3.3913999, -13.3786, 3.8388, 3.3913999, -16.7232, 6.6893, 3.3913999, -16.7232, 4.6821, 3.3913999, -13.3786, 4.0225, 3.3913999, -8.7143, 4.6821, 3.3913999, -13.3786, 4.6821, 3.3913999, -13.3786, 4.6821, 3.3913999, -13.3786, 4.4131, 3.2091, -13.357699, 4.6821, 3.3913999, -13.3786, 4.4131, 3.2091, -13.357699, 4.4131, 3.2091, -13.357699, 3.3446, 3.3913999, -6.6893, 4.0225, 3.3913999, -8.7143, 6.6893, 3.3913999, -10.033899, 3.3446, 3.3913999, -6.6893, 6.6893, 3.3913999, -10.033899, 6.6893, 3.3913999, -6.6893, 0, 3.3913999, -6.6893, 4.0225, 3.3913999, -8.7143, 3.3446, 3.3913999, -6.6893, 0, 3.3913999, -6.6893, 0, 3.3913999, -8.7143, 4.0225, 3.3913999, -8.7143, 3.3446, 3.3913999, -3.3446, 3.3446, 3.3913999, -6.6893, 6.6893, 3.3913999, -6.6893, 0, 3.3913999, -6.6893, -3.9101999, 3.3913999, -9.8592, 0, 3.3913999, -8.7143, 0, 3.3913999, -8.7143, -3.9101999, 3.3913999, -9.8592, 0, 3.3913999, -8.7143, 0, 3.3913999, -3.3446, 0, 3.3913999, -6.6893, 3.3446, 3.3913999, -6.6893, 0, 3.3913999, -3.3446, 3.3446, 3.3913999, -6.6893, 3.3446, 3.3913999, -3.3446, -3.6378, 3.3913999, -2.0792, 0, 3.3913999, -6.6893, 0, 3.3913999, -3.3446, -2.9566998, 3.3913999, 0, -3.6378, 3.3913999, -2.0792, 0, 3.3913999, -3.3446, -3.6378, 3.3913999, -2.0792, -3.3446, 3.3913999, -6.6893, 0, 3.3913999, -6.6893, -3.9101999, 3.3913999, -9.8592, 0, 3.3913999, -6.6893, -3.3446, 3.3913999, -6.6893, -6.6893, 3.3913999, -3.7326, -3.3446, 3.3913999, -6.6893, -3.6378, 3.3913999, -2.0792, -2.9566998, 3.3913999, 0, 0, 3.3913999, -3.3446, 0, 3.3913999, 0, 0, 3.3913999, 0, 0, 3.3913999, -3.3446, 3.3446, 3.3913999, -3.3446, -2.9566998, 3.3913999, 3.3446, -2.9566998, 3.3913999, 0, 0, 3.3913999, 0, -2.9566998, 3.3913999, 3.3446, 0, 3.3913999, 0, 0, 3.3913999, 3.3446, -6.6893, 3.3913999, -3.7326, -6.6893, 3.3913999, -6.6893, -3.3446, 3.3913999, -6.6893, -6.6893, 3.3913999, -6.6893, -3.9101999, 3.3913999, -9.8592, -3.3446, 3.3913999, -6.6893, -10.033899, 3.3913999, -3.7326, -6.6893, 3.3913999, -6.6893, -6.6893, 3.3913999, -3.7326, 0, 3.3913999, 3.3446, 0, 3.3913999, 0, 3.3446, 3.3913999, 0, 0, 3.3913999, 0, 3.3446, 3.3913999, -3.3446, 3.3446, 3.3913999, 0, 0, 3.3913999, 3.3446, 3.3446, 3.3913999, 0, 3.3446, 3.3913999, 3.3446, 3.3446, 3.3913999, 3.3446, 3.3446, 3.3913999, 0, 6.6893, 3.3913999, 0, 3.3446, 3.3913999, 0, 3.3446, 3.3913999, -3.3446, 6.6893, 3.3913999, -3.3446, 3.3446, 3.3913999, 0, 6.6893, 3.3913999, -3.3446, 6.6893, 3.3913999, 0, 3.3446, 3.3913999, -3.3446, 6.6893, 3.3913999, -6.6893, 6.6893, 3.3913999, -3.3446, 6.6893, 3.3913999, 0, 6.6893, 3.3913999, -3.3446, 10.033899, 3.3913999, -3.3446, 6.6893, 3.3913999, -3.3446, 6.6893, 3.3913999, -6.6893, 10.033899, 3.3913999, -6.6893, 6.6893, 3.3913999, -3.3446, 10.033899, 3.3913999, -6.6893, 10.033899, 3.3913999, -3.3446, -6.6893, 3.3913999, -6.6893, -6.6893, 3.3913999, -10.033899, -3.9101999, 3.3913999, -9.8592, -6.6893, 3.3913999, -10.033899, -3.9101999, 3.3913999, -12.538199, -3.9101999, 3.3913999, -9.8592, -10.033899, 3.3913999, -3.7326, -10.033899, 3.3913999, -6.6893, -6.6893, 3.3913999, -6.6893, -10.033899, 3.3913999, -6.6893, -6.6893, 3.3913999, -10.033899, -6.6893, 3.3913999, -6.6893, -13.3786, 3.3913999, -3.7326, -10.033899, 3.3913999, -6.6893, -10.033899, 3.3913999, -3.7326, -6.6893, 3.3913999, -10.033899, -6.6893, 3.3913999, -12.538199, -3.9101999, 3.3913999, -12.538199, -13.3786, 3.3913999, -3.7326, -13.3786, 3.3913999, -6.6893, -10.033899, 3.3913999, -6.6893, -16.191599, 3.3913999, -3.0971, -13.3786, 3.3913999, -6.6893, -13.3786, 3.3913999, -3.7326, -10.033899, 3.3913999, -6.6893, -10.033899, 3.3913999, -10.033899, -6.6893, 3.3913999, -10.033899, -13.3786, 3.3913999, -6.6893, -10.033899, 3.3913999, -10.033899, -10.033899, 3.3913999, -6.6893, -16.191599, 3.3913999, -3.0971, -16.7232, 3.3913999, -6.6893, -13.3786, 3.3913999, -6.6893, -13.3786, 3.3913999, -6.6893, -13.3786, 3.3913999, -10.033899, -10.033899, 3.3913999, -10.033899, -19.7545, 3.3913999, -2.7851999, -16.7232, 3.3913999, -6.6893, -16.191599, 3.3913999, -3.0971, -19.7545, 3.3913999, -2.7851999, -19.280699, 3.3913999, -6.6893, -16.7232, 3.3913999, -6.6893, -17.1112, 3.3913999, 0, -19.7545, 3.3913999, -2.7851999, -16.191599, 3.3913999, -3.0971, -17.1112, 3.3913999, 0, -19.9511, 3.3913999, -2.5834, -19.7545, 3.3913999, -2.7851999, -19.9511, 3.3913999, -2.5834, -17.1112, 3.3913999, 0, -20.0679, 3.3913999, 0, -20.0679, 3.3913999, 3.3446, -20.0679, 3.3913999, 0, -17.1112, 3.3913999, 0, -20.0679, 3.3913999, 3.3446, -17.1112, 3.3913999, 0, -17.1112, 3.3913999, 3.3044999, -13.3786, 3.3913999, -10.033899, -10.699599, 3.3913999, -13.175799, -10.033899, 3.3913999, -10.033899, -10.699599, 3.3913999, -13.175799, -6.6893, 3.3913999, -10.033899, -10.033899, 3.3913999, -10.033899, -13.3786, 3.3913999, -10.033899, -13.3786, 3.3913999, -13.3786, -10.699599, 3.3913999, -13.175799, -6.6893, 3.3913999, -10.033899, -10.699599, 3.3913999, -13.175799, -6.6893, 3.3913999, -12.538199, -13.3786, 3.3913999, -13.3786, -10.699599, 3.3913999, -15.835, -10.699599, 3.3913999, -13.175799, -13.3786, 3.3913999, -13.3786, -13.3786, 3.3913999, -15.4154, -10.699599, 3.3913999, -15.835, -6.6893, 3.3913999, -12.538199, -10.699599, 3.3913999, -13.175799, -6.6893, 3.3913999, -12.538199, -6.6893, 3.3913999, -12.538199, -6.6893, 3.3913999, -12.538199, -6.6893, 3.175, -12.867, -6.6893, 3.3913999, -12.538199, -6.6893, 3.175, -12.867, -6.6893, 3.175, -12.867, -20.0679, 3.3913999, 6.6893, -20.0679, 3.3913999, 3.3446, -17.1112, 3.3913999, 3.3044999, -22.5541, 3.3913999, 9.2058, -22.3151, 3.3913999, 6.6893, -20.0679, 3.3913999, 6.6893, -22.5541, 3.3913999, 9.2058, -20.0679, 3.3913999, 6.6893, -20.0679, 3.3913999, 9.167399, -20.0679, 3.3913999, 9.167399, -20.0679, 3.3913999, 6.6893, -16.7232, 3.3913999, 6.6893, -20.0679, 3.3913999, 6.6893, -17.1112, 3.3913999, 3.3044999, -16.7232, 3.3913999, 6.6893, -20.0679, 3.3913999, 9.167399, -16.7232, 3.3913999, 6.6893, -16.285099, 3.3913999, 9.5865, -16.7232, 3.3913999, 6.6893, -16.053999, 3.3913999, 9.8082, -16.285099, 3.3913999, 9.5865, -16.053999, 3.3913999, 9.8082, -16.7232, 3.3913999, 6.6893, -13.1355, 3.3913999, 6.7549996, -17.1112, 3.3913999, 3.3044999, -13.1355, 3.3913999, 6.7549996, -16.7232, 3.3913999, 6.6893, -16.053999, 3.3913999, 9.8082, -13.1355, 3.3913999, 6.7549996, -13.3786, 3.3913999, 10.033899, -13.1355, 3.3913999, 6.7549996, -17.1112, 3.3913999, 3.3044999, -14.877199, 3.3913999, 4.6657, -14.877199, 3.3913999, 4.6657, -17.1112, 3.3913999, 3.3044999, -15.582299, 3.3913999, 4.0366, -13.1355, 3.3913999, 6.7549996, -14.877199, 3.3913999, 4.6657, -14.0948, 3.3913999, 5.5073, -13.3786, 3.3913999, 10.033899, -13.1355, 3.3913999, 6.7549996, -10.033899, 3.3913999, 7.0772, -13.3786, 3.3913999, 10.033899, -10.033899, 3.3913999, 7.0772, -10.033899, 3.3913999, 10.033899, -10.033899, 3.3913999, 10.033899, -10.033899, 3.3913999, 7.0772, -6.6893, 3.3913999, 7.0772, -10.033899, 3.3913999, 10.033899, -6.6893, 3.3913999, 7.0772, -6.6893, 3.3913999, 10.033899, -10.033899, 3.0033998, 6.6893, -13.1355, 3.3913999, 6.7549996, -12.747499, 3.0033998, 6.3671, -10.033899, 3.0033998, 6.6893, -10.033899, 3.3913999, 7.0772, -13.1355, 3.3913999, 6.7549996, -2.5674999, 2.4164, 7.6215, -6.6893, 3.3913999, 7.0772, -6.6893, 3.0033998, 6.6893, -2.5674999, 2.4164, 7.6215, -2.1794999, 2.8042998, 8.009399, -6.6893, 3.3913999, 7.0772, -12.747499, 3.0033998, 6.3671, -14.0948, 3.3913999, 5.5073, -13.6942, 3.0033998, 5.1798, -12.747499, 3.0033998, 6.3671, -13.1355, 3.3913999, 6.7549996, -14.0948, 3.3913999, 5.5073, -6.6893, 3.0033998, 6.6893, -10.033899, 3.3913999, 7.0772, -10.033899, 3.0033998, 6.6893, -6.6893, 3.0033998, 6.6893, -6.6893, 3.3913999, 7.0772, -10.033899, 3.3913999, 7.0772, -16.7232, 3.0033998, 0, -16.191599, 3.3913999, -3.0971, -15.8036995, 3.0033998, -2.7091, -16.7232, 3.0033998, 0, -17.1112, 3.3913999, 0, -16.191599, 3.3913999, -3.0971, -13.3786, 3.3913999, -3.7326, -15.8036995, 3.0033998, -2.7091, -16.191599, 3.3913999, -3.0971, -13.3786, 3.3913999, -3.7326, -13.3786, 3.0033998, -3.3446, -15.8036995, 3.0033998, -2.7091, -2.9566998, 3.3913999, 0, -4.0257998, 3.0033998, -1.6911999, -3.6378, 3.3913999, -2.0792, -2.9566998, 3.3913999, 0, -3.3446, 3.0033998, 0, -4.0257998, 3.0033998, -1.6911999, -3.6378, 3.3913999, -2.0792, -6.6893, 3.0033998, -3.3446, -6.6893, 3.3913999, -3.7326, -3.6378, 3.3913999, -2.0792, -4.0257998, 3.0033998, -1.6911999, -6.6893, 3.0033998, -3.3446, -10.033899, 3.3913999, -3.7326, -13.3786, 3.0033998, -3.3446, -13.3786, 3.3913999, -3.7326, -10.033899, 3.3913999, -3.7326, -10.033899, 3.0033998, -3.3446, -13.3786, 3.0033998, -3.3446, -6.6893, 3.3913999, -3.7326, -10.033899, 3.0033998, -3.3446, -10.033899, 3.3913999, -3.7326, -6.6893, 3.3913999, -3.7326, -6.6893, 3.0033998, -3.3446, -10.033899, 3.0033998, -3.3446, -16.7232, 3.0033998, 2.9166, -17.1112, 3.3913999, 0, -16.7232, 3.0033998, 0, -16.7232, 3.0033998, 2.9166, -17.1112, 3.3913999, 3.3044999, -17.1112, 3.3913999, 0, -14.4892, 3.0033998, 4.2778, -15.582299, 3.3913999, 4.0366, -15.372, 3.0033998, 3.6132998, -14.4892, 3.0033998, 4.2778, -14.877199, 3.3913999, 4.6657, -15.582299, 3.3913999, 4.0366, -2.9566998, 3.3913999, 3.3446, -3.3446, 3.0033998, 0, -2.9566998, 3.3913999, 0, -2.9566998, 3.3913999, 3.3446, -3.3446, 3.0033998, 3.3446, -3.3446, 3.0033998, 0, -2.1794999, 2.8042998, 8.009399, -2.1397, 3.0033998, 5.1795, -1.7832999, 3.3913999, 5.2162, -2.1794999, 2.8042998, 8.009399, -2.5674999, 2.4164, 7.6215, -2.1397, 3.0033998, 5.1795, -6.6893, 3.3913999, 10.033899, -2.1794999, 2.8042998, 8.009399, -3.3446, 3.3913999, 10.033899, -6.6893, 3.3913999, 10.033899, -6.6893, 3.3913999, 7.0772, -2.1794999, 2.8042998, 8.009399, -1.7832999, 3.3913999, 5.2162, 0, 3.3913999, 6.6893, -2.1794999, 2.8042998, 8.009399, 0, 3.3913999, 6.6893, -1.7832999, 3.3913999, 5.2162, -2.9566998, 3.3913999, 3.3446, 0, 3.3913999, 6.6893, -2.9566998, 3.3913999, 3.3446, 0, 3.3913999, 3.3446, -3.3446, 3.3913999, 10.033899, 0, 3.3913999, 6.6893, 0, 3.3913999, 10.033899, -3.3446, 3.3913999, 10.033899, -2.1794999, 2.8042998, 8.009399, 0, 3.3913999, 6.6893, -2.1397, 3.0033998, 5.1795, -2.9566998, 3.3913999, 3.3446, -1.7832999, 3.3913999, 5.2162, -2.1397, 3.0033998, 5.1795, -3.3446, 3.0033998, 3.3446, -2.9566998, 3.3913999, 3.3446, -14.0948, 3.3913999, 5.5073, -14.4892, 3.0033998, 4.2778, -13.6942, 3.0033998, 5.1798, -14.0948, 3.3913999, 5.5073, -14.877199, 3.3913999, 4.6657, -14.4892, 3.0033998, 4.2778, -15.582299, 3.3913999, 4.0366, -16.7232, 3.0033998, 2.9166, -15.372, 3.0033998, 3.6132998, -15.582299, 3.3913999, 4.0366, -17.1112, 3.3913999, 3.3044999, -16.7232, 3.0033998, 2.9166, 20.198, 3.1490998, -10.033899, 19.798899, 3.3913999, -13.1096, 20.1539, 3.1299, -13.399799, 20.198, 3.1490998, -10.033899, 19.798899, 3.3913999, -10.033899, 19.798899, 3.3913999, -13.1096, 20.1539, 3.1299, -13.399799, 16.7232, 3.3913999, -13.1096, 16.8204, 3.138, -13.447599, 20.1539, 3.1299, -13.399799, 19.798899, 3.3913999, -13.1096, 16.7232, 3.3913999, -13.1096, 16.756899, 3.1650999, -16.7286, 12.738999, 3.3913999, -15.894899, 12.7471, 3.1766999, -16.1988, 16.756899, 3.1650999, -16.7286, 16.495699, 3.3913999, -16.459, 12.738999, 3.3913999, -15.894899, 16.8051, 3.1778, -13.479, 16.495699, 3.3913999, -16.459, 16.756899, 3.1650999, -16.7286, 16.8051, 3.1778, -13.479, 16.495699, 3.3913999, -13.42, 16.495699, 3.3913999, -16.459, 16.4543, 3.3913999, -13.3786, 16.8204, 3.138, -13.447599, 16.7232, 3.3913999, -13.1096, 16.8204, 3.138, -13.447599, 16.4543, 3.3913999, -13.3786, 16.495699, 3.3913999, -13.42, 16.8204, 3.138, -13.447599, 16.495699, 3.3913999, -13.42, 16.8051, 3.1778, -13.479, 10.033899, 3.0523999, -19.0294, 6.6893, 3.2782998, -18.727999, 6.6893, 3.0440998, -19.036999, 10.033899, 3.0523999, -19.0294, 10.033899, 3.2784998, -18.6997, 6.6893, 3.2782998, -18.727999, 12.723599, 2.9213, -19.2902, 10.033899, 3.2784998, -18.6997, 10.033899, 3.0523999, -19.0294, 12.723599, 2.9213, -19.2902, 12.446099, 3.1699998, -18.9401, 10.033899, 3.2784998, -18.6997, 12.446099, 3.1699998, -18.9401, 12.747, 3.1771, -16.1991, 12.460899, 3.3913999, -16.1674, 12.446099, 3.1699998, -18.9401, 12.723599, 2.9213, -19.2902, 12.747, 3.1771, -16.1991, 12.460899, 3.3913999, -16.1674, 10.033899, 3.2784998, -18.6997, 12.446099, 3.1699998, -18.9401, 10.033899, 3.3913999, -16.7232, 10.033899, 3.2784998, -18.6997, 12.460899, 3.3913999, -16.1674, 10.033899, 3.3913999, -16.7232, 12.460899, 3.3913999, -16.1674, 12.460899, 3.3913999, -16.1674, 6.6893, 3.3913999, -16.7232, 10.033899, 3.2784998, -18.6997, 10.033899, 3.3913999, -16.7232, 6.6893, 3.3913999, -16.7232, 6.6893, 3.2782998, -18.727999, 10.033899, 3.2784998, -18.6997, 12.738999, 3.3913999, -15.894899, 12.747, 3.1771, -16.1991, 12.7471, 3.1766999, -16.1988, 12.738999, 3.3913999, -15.894899, 12.460899, 3.3913999, -16.1674, 12.747, 3.1771, -16.1991, 12.460899, 3.3913999, -16.1674, 12.738999, 3.3913999, -15.894899, 12.460899, 3.3913999, -16.1674, 3.8388, 3.3913999, -16.7232, 4.4783998, 3.1067, -18.2665, 4.6443, 3.2897, -17.9799, 3.8388, 3.3913999, -16.7232, 3.5895, 3.2033, -16.7232, 4.4783998, 3.1067, -18.2665, 4.6821, 3.3913999, -13.3786, 3.5895, 3.2033, -16.7232, 3.8388, 3.3913999, -16.7232, 4.6821, 3.3913999, -13.3786, 4.4131, 3.2091, -13.357699, 3.5895, 3.2033, -16.7232, 6.6640997, 3.0702, -19.018099, 4.6443, 3.2897, -17.9799, 4.4783998, 3.1067, -18.2665, 6.6640997, 3.0702, -19.018099, 6.6705, 3.2772, -18.741, 4.6443, 3.2897, -17.9799, 4.6443, 3.2897, -17.9799, 6.6893, 3.3913999, -16.7232, 3.8388, 3.3913999, -16.7232, 6.6893, 3.3913999, -16.7232, 4.6443, 3.2897, -17.9799, 6.6705, 3.2772, -18.741, 6.6893, 3.3913999, -16.7232, 6.6705, 3.2772, -18.741, 6.6893, 3.2782998, -18.727999, 6.6893, 3.2782998, -18.727999, 6.6640997, 3.0702, -19.018099, 6.6893, 3.0440998, -19.036999, 6.6893, 3.2782998, -18.727999, 6.6705, 3.2772, -18.741, 6.6640997, 3.0702, -19.018099, 3.7529, 3.1712, -9.0494995, 0, 3.3913999, -8.7143, 0, 3.179, -9.0652, 3.7529, 3.1712, -9.0494995, 4.0225, 3.3913999, -8.7143, 0, 3.3913999, -8.7143, 4.0225, 3.3913999, -8.7143, 4.4131, 3.2091, -13.357699, 4.6821, 3.3913999, -13.3786, 4.0225, 3.3913999, -8.7143, 3.7529, 3.1712, -9.0494995, 4.4131, 3.2091, -13.357699, -3.6538, 3.1585999, -12.813399, -6.6893, 3.3913999, -12.538199, -6.6893, 3.175, -12.867, -3.6538, 3.1585999, -12.813399, -3.9101999, 3.3913999, -12.538199, -6.6893, 3.3913999, -12.538199, -3.6076, 3.1676, -10.0696, -3.9101999, 3.3913999, -12.538199, -3.6538, 3.1585999, -12.813399, -3.6076, 3.1676, -10.0696, -3.9101999, 3.3913999, -9.8592, -3.9101999, 3.3913999, -12.538199, -0.0081, 3.1864998, -9.063499, -3.9101999, 3.3913999, -9.8592, -3.6076, 3.1676, -10.0696, -0.0081, 3.1864998, -9.063499, 0, 3.3913999, -8.7143, -3.9101999, 3.3913999, -9.8592, 0, 3.3913999, -8.7143, -0.0081, 3.1864998, -9.063499, 0, 3.179, -9.0652, 0, 3.3913999, -8.7143, 0, 3.3913999, -8.7143, -0.0081, 3.1864998, -9.063499, -10.4043, 3.1608999, -13.4295, -10.699599, 3.3913999, -15.835, -10.4319, 3.1557999, -16.1556, -10.4043, 3.1608999, -13.4295, -10.699599, 3.3913999, -13.175799, -10.699599, 3.3913999, -15.835, -10.4319, 3.1557999, -16.1556, -13.3786, 3.3913999, -15.4154, -13.3786, 3.1771998, -15.7625, -10.4319, 3.1557999, -16.1556, -10.699599, 3.3913999, -15.835, -13.3786, 3.3913999, -15.4154, -10.699599, 3.3913999, -13.175799, -6.6893, 3.175, -12.867, -6.6893, 3.3913999, -12.538199, -10.699599, 3.3913999, -13.175799, -10.4043, 3.1608999, -13.4295, -6.6893, 3.175, -12.867, -16.130499, 3.6227, -13.623799, -16.0243, 3.3509998, -15.752, -15.7941, 3.5591, -15.4641, -16.130499, 3.6227, -13.623799, -16.3753, 3.4225, -13.6277, -16.0243, 3.3509998, -15.752, -13.3911, 3.1892, -15.757, -15.7941, 3.5591, -15.4641, -16.0243, 3.3509998, -15.752, -13.3911, 3.1892, -15.757, -13.396, 3.3925, -15.4333, -15.7941, 3.5591, -15.4641, -15.7941, 3.5591, -15.4641, -13.3786, 3.3913999, -13.3786, -16.130499, 3.6227, -13.623799, -13.3786, 3.3913999, -13.3786, -15.7941, 3.5591, -15.4641, -13.396, 3.3925, -15.4333, -13.3786, 3.3913999, -13.3786, -13.396, 3.3925, -15.4333, -13.3786, 3.3913999, -15.4154, -13.3786, 3.3913999, -15.4154, -13.3911, 3.1892, -15.757, -13.3786, 3.1771998, -15.7625, -13.3786, 3.3913999, -15.4154, -13.396, 3.3925, -15.4333, -13.3911, 3.1892, -15.757, -16.3753, 3.4225, -13.6277, -16.1241, 3.3832998, -9.5097, -16.4131, 3.1706, -9.788899, -16.3753, 3.4225, -13.6277, -16.130499, 3.6227, -13.623799, -16.1241, 3.3832998, -9.5097, -16.7232, 3.3913999, -6.6893, -13.3786, 3.3913999, -10.033899, -13.3786, 3.3913999, -6.6893, -16.7232, 3.3913999, -6.6893, -16.1241, 3.3832998, -9.5097, -13.3786, 3.3913999, -10.033899, -16.1241, 3.3832998, -9.5097, -13.3786, 3.3913999, -13.3786, -13.3786, 3.3913999, -10.033899, -16.1241, 3.3832998, -9.5097, -16.130499, 3.6227, -13.623799, -13.3786, 3.3913999, -13.3786, -16.130499, 3.6227, -13.623799, -16.1241, 3.3832998, -9.5097, -16.130499, 3.6227, -13.623799, -16.130499, 3.6227, -13.623799, -16.3753, 3.4225, -13.6277, -16.3753, 3.4225, -13.6277, -16.130499, 3.6227, -13.623799, -16.130499, 3.6227, -13.623799, -16.3753, 3.4225, -13.6277, -19.7545, 3.3913999, -2.7851999, -19.515, 3.2122, -6.6893, -19.280699, 3.3913999, -6.6893, -19.7545, 3.3913999, -2.7851999, -19.9489, 3.2063, -2.8082, -19.515, 3.2122, -6.6893, -19.280699, 3.3913999, -6.6893, -19.6161, 3.2075999, -9.7116995, -19.3965, 3.3909, -9.5092, -19.280699, 3.3913999, -6.6893, -19.515, 3.2122, -6.6893, -19.6161, 3.2075999, -9.7116995, -19.280699, 3.3913999, -6.6893, -16.1241, 3.3832998, -9.5097, -16.7232, 3.3913999, -6.6893, -19.280699, 3.3913999, -6.6893, -19.3965, 3.3909, -9.5092, -16.1241, 3.3832998, -9.5097, -16.1241, 3.3832998, -9.5097, -19.6237, 3.1655998, -9.757899, -16.4131, 3.1706, -9.788899, -19.6237, 3.1655998, -9.757899, -16.1241, 3.3832998, -9.5097, -19.3965, 3.3909, -9.5092, -19.6237, 3.1655998, -9.757899, -19.3965, 3.3909, -9.5092, -19.6161, 3.2075999, -9.7116995, -22.8657, 3.4937, 3.3613, -23.3676, 3.4459999, 0.1372, -23.170399, 3.6251, 0.122999996, -22.8657, 3.4937, 3.3613, -23.0809, 3.3083, 3.3615, -23.3676, 3.4459999, 0.1372, -23.170399, 3.6251, 0.122999996, -23.390299, 3.3349998, -2.6938999, -23.192099, 3.5300999, -2.4668999, -23.170399, 3.6251, 0.122999996, -23.3676, 3.4459999, 0.1372, -23.390299, 3.3349998, -2.6938999, -23.192099, 3.5300999, -2.4668999, -19.9489, 3.2063, -2.8082, -19.9511, 3.3913999, -2.5834, -23.192099, 3.5300999, -2.4668999, -23.390299, 3.3349998, -2.6938999, -19.9489, 3.2063, -2.8082, -23.192099, 3.5300999, -2.4668999, -20.0679, 3.3913999, 0, -23.170399, 3.6251, 0.122999996, -20.0679, 3.3913999, 0, -23.192099, 3.5300999, -2.4668999, -19.9511, 3.3913999, -2.5834, -19.9511, 3.3913999, -2.5834, -23.192099, 3.5300999, -2.4668999, -19.9511, 3.3913999, -2.5834, -22.8657, 3.4937, 3.3613, -20.0679, 3.3913999, 0, -20.0679, 3.3913999, 3.3446, -22.8657, 3.4937, 3.3613, -23.170399, 3.6251, 0.122999996, -20.0679, 3.3913999, 0, -19.7545, 3.3913999, -2.7851999, -19.9489, 3.2063, -2.8082, -19.9489, 3.2063, -2.8082, -19.7545, 3.3913999, -2.7851999, -19.9511, 3.3913999, -2.5834, -19.9489, 3.2063, -2.8082, -19.9511, 3.3913999, -2.5834, -19.7545, 3.3913999, -2.7851999, -19.9511, 3.3913999, -2.5834, -23.0777, 3.3197, 3.3734, -22.3151, 3.3913999, 6.6893, -22.563799, 3.2219, 6.6893, -23.0777, 3.3197, 3.3734, -22.8764, 3.4937, 3.3741, -22.3151, 3.3913999, 6.6893, -22.8764, 3.4937, 3.3741, -20.0679, 3.3913999, 6.6893, -22.3151, 3.3913999, 6.6893, -20.0679, 3.3913999, 6.6893, -22.8764, 3.4937, 3.3741, -22.8657, 3.4937, 3.3613, -20.0679, 3.3913999, 6.6893, -22.8657, 3.4937, 3.3613, -20.0679, 3.3913999, 3.3446, -22.8657, 3.4937, 3.3613, -23.0777, 3.3197, 3.3734, -23.0809, 3.3083, 3.3615, -22.8657, 3.4937, 3.3613, -22.8764, 3.4937, 3.3741, -23.0777, 3.3197, 3.3734, -16.285099, 3.3913999, 9.5865, -20.0679, 3.1943, 9.407499, -20.0679, 3.3913999, 9.167399, -16.285099, 3.3913999, 9.5865, -16.2818, 3.1913998, 9.8056, -20.0679, 3.1943, 9.407499, -22.791399, 3.2124999, 9.4432, -20.0679, 3.3913999, 9.167399, -20.0679, 3.1943, 9.407499, -22.791399, 3.2124999, 9.4432, -22.5541, 3.3913999, 9.2058, -20.0679, 3.3913999, 9.167399, -22.5541, 3.3913999, 9.2058, -22.563799, 3.2219, 6.6893, -22.3151, 3.3913999, 6.6893, -22.5541, 3.3913999, 9.2058, -22.791399, 3.2124999, 9.4432, -22.563799, 3.2219, 6.6893, -22.791399, 3.2124999, 9.4432, -22.5541, 3.3913999, 9.2058, -22.791399, 3.2124999, 9.4432, -13.3786, 3.3922, 12.571899, -16.076399, 3.04, 12.735, -15.847199, 3.2614, 12.4973, -13.3786, 3.3922, 12.571899, -13.3786, 3.194, 12.8089, -16.076399, 3.04, 12.735, -15.847199, 3.2614, 12.4973, -16.2818, 3.1913998, 9.8056, -16.053999, 3.3913999, 9.8082, -15.847199, 3.2614, 12.4973, -16.076399, 3.04, 12.735, -16.2818, 3.1913998, 9.8056, -15.847199, 3.2614, 12.4973, -13.3786, 3.3913999, 10.033899, -13.3786, 3.3922, 12.571899, -15.847199, 3.2614, 12.4973, -16.053999, 3.3913999, 9.8082, -13.3786, 3.3913999, 10.033899, -16.053999, 3.3913999, 9.8082, -15.847199, 3.2614, 12.4973, -16.053999, 3.3913999, 9.8082, -16.285099, 3.3913999, 9.5865, -16.2818, 3.1913998, 9.8056, -16.2818, 3.1913998, 9.8056, -16.285099, 3.3913999, 9.5865, -16.053999, 3.3913999, 9.8082, -16.2818, 3.1913998, 9.8056, -16.053999, 3.3913999, 9.8082, -16.285099, 3.3913999, 9.5865, -16.053999, 3.3913999, 9.8082, -5.9742, 3.3994, 12.8817, -10.033899, 2.9384, 12.4537, -10.033899, 3.2142, 12.1061, -5.9742, 3.3994, 12.8817, -6.2748, 3.126, 13.122, -10.033899, 2.9384, 12.4537, -10.033899, 3.2142, 12.1061, -13.3786, 3.194, 12.8089, -13.3786, 3.3922, 12.571899, -10.033899, 3.2142, 12.1061, -10.033899, 2.9384, 12.4537, -13.3786, 3.194, 12.8089, -10.033899, 3.2142, 12.1061, -6.6893, 3.3913999, 10.033899, -5.9742, 3.3994, 12.8817, -10.033899, 3.2142, 12.1061, -10.033899, 3.3913999, 10.033899, -6.6893, 3.3913999, 10.033899, -13.3786, 3.3922, 12.571899, -10.033899, 3.3913999, 10.033899, -10.033899, 3.2142, 12.1061, -13.3786, 3.3922, 12.571899, -13.3786, 3.3913999, 10.033899, -10.033899, 3.3913999, 10.033899, -13.3786, 3.3913999, 10.033899, -13.3786, 3.3922, 12.571899, -13.3786, 3.3922, 12.571899, -5.9742, 3.3994, 12.8817, -3.3446, 3.3913999, 10.033899, -3.3446, 3.3913999, 13.3786, -5.9742, 3.3994, 12.8817, -6.6893, 3.3913999, 10.033899, -3.3446, 3.3913999, 10.033899, -13.3786, 3.3922, 12.571899, -13.3786, 3.194, 12.8089, -13.3786, 3.194, 12.8089, -13.3786, 3.3922, 12.571899, -13.3786, 3.3922, 12.571899, -13.3786, 3.194, 12.8089, 3.6399, 3.3913999, 16.2405, 0, 2.9636998, 16.0179, 0, 3.2243998, 15.7093, 3.6399, 3.3913999, 16.2405, 3.6425998, 3.1413999, 16.5127, 0, 2.9636998, 16.0179, 0, 3.2243998, 15.7093, -3.3446, 3.1497998, 15.914599, -3.3446, 3.3913999, 15.6011, 0, 3.2243998, 15.7093, 0, 2.9636998, 16.0179, -3.3446, 3.1497998, 15.914599, -6.2558, 3.1427999, 16.4098, -3.3446, 3.3913999, 15.6011, -3.3446, 3.1497998, 15.914599, -6.2558, 3.1427999, 16.4098, -5.9737997, 3.392, 16.087599, -3.3446, 3.3913999, 15.6011, -3.3446, 3.3913999, 15.6011, 0, 3.3913999, 13.3786, 0, 3.2243998, 15.7093, -3.3446, 3.3913999, 15.6011, -3.3446, 3.3913999, 13.3786, 0, 3.3913999, 13.3786, 0, 3.2243998, 15.7093, 3.3446, 3.3913999, 13.3786, 3.6399, 3.3913999, 16.2405, 0, 3.2243998, 15.7093, 0, 3.3913999, 13.3786, 3.3446, 3.3913999, 13.3786, -5.9737997, 3.392, 16.087599, -3.3446, 3.3913999, 13.3786, -3.3446, 3.3913999, 15.6011, -5.9737997, 3.392, 16.087599, -5.9742, 3.3994, 12.8817, -3.3446, 3.3913999, 13.3786, -6.2748, 3.1260998, 16.4145, -5.9737997, 3.392, 16.087599, -6.2558, 3.1427999, 16.4098, -5.9737997, 3.392, 16.087599, -6.2748, 3.1260998, 16.4145, -6.2748, 3.126, 13.122, -5.9737997, 3.392, 16.087599, -6.2748, 3.126, 13.122, -5.9742, 3.3994, 12.8817, 6.6893, 3.3913999, 18.9861, 3.9266999, 3.5967, 19.6223, 4.1273, 3.7335, 19.3804, 6.6893, 3.3913999, 18.9861, 6.6893, 3.2050998, 19.228699, 3.9266999, 3.5967, 19.6223, 4.1273, 3.7335, 19.3804, 3.6536, 3.2078998, 16.5669, 3.8651, 3.3981998, 16.5647, 4.1273, 3.7335, 19.3804, 3.9266999, 3.5967, 19.6223, 3.6536, 3.2078998, 16.5669, 3.8651, 3.3981998, 16.5647, 6.6893, 3.3913999, 18.9861, 4.1273, 3.7335, 19.3804, 6.6893, 3.3913999, 18.9861, 3.8651, 3.3981998, 16.5647, 3.9148, 3.3913999, 16.514599, 6.6893, 3.3913999, 18.9861, 3.9148, 3.3913999, 16.514599, 6.6893, 3.3913999, 16.7232, 3.9148, 3.3913999, 16.514599, 3.6425998, 3.1413999, 16.5127, 3.6399, 3.3913999, 16.2405, 3.6425998, 3.1413999, 16.5127, 3.9148, 3.3913999, 16.514599, 3.8651, 3.3981998, 16.5647, 3.6425998, 3.1413999, 16.5127, 3.8651, 3.3981998, 16.5647, 3.6536, 3.2078998, 16.5669, 9.3066, 3.2186, 19.0653, 10.3743, 3.3913999, 16.7232, 10.5913, 3.1985998, 16.7232, 9.3066, 3.2186, 19.0653, 9.1275, 3.3913999, 18.830599, 10.3743, 3.3913999, 16.7232, 9.1275, 3.3913999, 18.830599, 6.6991997, 3.2148, 19.2255, 6.6949, 3.3913999, 18.9914, 9.1275, 3.3913999, 18.830599, 9.3066, 3.2186, 19.0653, 6.6991997, 3.2148, 19.2255, 6.6893, 3.3913999, 18.9861, 6.6991997, 3.2148, 19.2255, 6.6893, 3.2050998, 19.228699, 6.6893, 3.3913999, 18.9861, 6.6949, 3.3913999, 18.9914, 6.6991997, 3.2148, 19.2255, 9.8131, 3.3546, 13.1979, 10.5893, 3.2117, 16.7106, 10.403999, 3.3911998, 16.6938, 9.8131, 3.3546, 13.1979, 10.0546, 3.1885, 13.4071, 10.5893, 3.2117, 16.7106, 6.6893, 3.3913999, 13.3786, 10.3743, 3.3913999, 16.7232, 6.6893, 3.3913999, 16.7232, 10.3743, 3.3913999, 16.7232, 6.6893, 3.3913999, 13.3786, 9.8131, 3.3546, 13.1979, 10.3743, 3.3913999, 16.7232, 9.8131, 3.3546, 13.1979, 10.403999, 3.3911998, 16.6938, 6.6893, 3.3913999, 13.3786, 10.033899, 3.3913999, 10.033899, 9.8131, 3.3546, 13.1979, 6.6893, 3.3913999, 13.3786, 6.6893, 3.3913999, 10.033899, 10.033899, 3.3913999, 10.033899, 10.3743, 3.3913999, 16.7232, 10.5893, 3.2117, 16.7106, 10.5913, 3.1985998, 16.7232, 10.3743, 3.3913999, 16.7232, 10.403999, 3.3911998, 16.6938, 10.5893, 3.2117, 16.7106, 13.1839, 3.2744, 6.6893, 13.5511, 3.1797, 6.6893, 13.4036, 3.0749998, 6.6893, 13.1839, 3.2744, 6.6893, 13.3786, 3.2744, 6.496, 13.5511, 3.1797, 6.6893, 12.9758, 3.2036, 10.033899, 13.1839, 3.2744, 6.6893, 13.4036, 3.0749998, 6.6893, 12.9758, 3.2036, 10.033899, 12.7325, 3.3913999, 10.033899, 13.1839, 3.2744, 6.6893, 13.2631, 3.5611, 13.4043, 12.7325, 3.3913999, 10.033899, 12.9758, 3.2036, 10.033899, 13.2631, 3.5611, 13.4043, 13.025499, 3.6999, 13.1985, 12.7325, 3.3913999, 10.033899, 10.033899, 3.3913999, 10.033899, 13.1839, 3.2744, 6.6893, 12.7325, 3.3913999, 10.033899, 10.033899, 3.3913999, 10.033899, 10.033899, 3.3913999, 6.6893, 13.1839, 3.2744, 6.6893, 9.8131, 3.3546, 13.1979, 12.7325, 3.3913999, 10.033899, 13.025499, 3.6999, 13.1985, 9.8131, 3.3546, 13.1979, 10.033899, 3.3913999, 10.033899, 12.7325, 3.3913999, 10.033899, 10.033899, 3.3913999, 3.3446, 13.1839, 3.2744, 6.6893, 10.033899, 3.3913999, 6.6893, 13.1839, 3.2744, 6.6893, 10.033899, 3.3913999, 3.3446, 13.3786, 3.3913999, 3.3446, 13.1839, 3.2744, 6.6893, 13.3786, 3.3913999, 3.3446, 13.3786, 3.2744, 6.496, 13.025499, 3.6999, 13.1985, 10.0546, 3.1885, 13.4071, 9.8131, 3.3546, 13.1979, 13.025499, 3.6999, 13.1985, 13.2631, 3.5611, 13.4043, 10.0546, 3.1885, 13.4071, 13.2631, 3.5611, 13.4043, 13.2631, 3.5611, 13.4043, 13.025499, 3.6999, 13.1985, 14.0489, 3.0042, -3.3446, 13.1206, 3.3028998, -3.3446, 13.3786, 3.3028998, -3.6027, 14.0489, 3.0042, -3.3446, 13.3786, 3.3028998, -3.0865998, 13.1206, 3.3028998, -3.3446, 16.7232, 3.1513, -3.602, 14.0489, 3.0042, -3.3446, 13.3786, 3.3028998, -3.6027, 16.7232, 3.1513, -3.602, 16.7127, 2.8734, -3.3446, 14.0489, 3.0042, -3.3446, 13.3786, 3.3028998, -3.6027, 16.7232, 3.3913999, -6.6893, 16.7232, 3.1513, -3.602, 13.3786, 3.3028998, -3.6027, 13.3786, 3.3913999, -6.6893, 16.7232, 3.3913999, -6.6893, 10.033899, 3.3913999, -6.6893, 13.1206, 3.3028998, -3.3446, 10.033899, 3.3913999, -3.3446, 13.1206, 3.3028998, -3.3446, 10.033899, 3.3913999, -6.6893, 13.3786, 3.3913999, -6.6893, 13.1206, 3.3028998, -3.3446, 13.3786, 3.3913999, -6.6893, 13.3786, 3.3028998, -3.6027, 20.0679, 0, 0, 14.0489, 3.0042, -3.3446, 20.961199, 0, -3.3446, 20.0679, 0, 3.3446, 14.0489, 3.0042, -3.3446, 20.0679, 0, 0, 14.0489, 3.0042, -3.3446, 13.3786, 3.3913999, 0, 13.3786, 3.3028998, -3.0865998, 14.0489, 3.0042, -3.3446, 13.3786, 3.2744, 6.496, 13.3786, 3.3913999, 0, 14.0489, 3.0042, -3.3446, 20.0679, 0, 3.3446, 13.3786, 3.2744, 6.496, 13.3786, 3.3913999, 0, 13.3786, 3.2744, 6.496, 13.3786, 3.3913999, 3.3446, 13.3786, 3.2744, 6.496, 20.0679, 0, 3.3446, 16.7232, 1.5715, 6.6893, 16.7232, 1.5715, 6.6893, 20.0679, 0, 3.3446, 20.0679, -0.124199994, 6.6893, 13.3786, 3.2744, 6.496, 16.7232, 1.5715, 6.6893, 13.5511, 3.1797, 6.6893, 10.033899, 3.3913999, -3.3446, 13.3786, 3.3913999, 0, 10.033899, 3.3913999, 0, 13.3786, 3.3913999, 0, 10.033899, 3.3913999, -3.3446, 13.1206, 3.3028998, -3.3446, 13.3786, 3.3913999, 0, 13.1206, 3.3028998, -3.3446, 13.3786, 3.3028998, -3.0865998, 19.831999, 3.3902998, -10.0008, 20.147, 3.0588999, -6.6893, 19.8322, 3.2891998, -6.6893, 19.831999, 3.3902998, -10.0008, 20.1796, 3.182, -9.995999, 20.147, 3.0588999, -6.6893, 19.8322, 3.2891998, -6.6893, 20.1349, 2.7974, -3.3783998, 19.8322, 3.0491, -3.602, 19.8322, 3.2891998, -6.6893, 20.147, 3.0588999, -6.6893, 20.1349, 2.7974, -3.3783998, 16.7232, 3.1513, -3.602, 19.8322, 3.2891998, -6.6893, 19.8322, 3.0491, -3.602, 16.7232, 3.1513, -3.602, 16.7232, 3.3913999, -6.6893, 19.8322, 3.2891998, -6.6893, 16.7232, 3.3913999, -10.033899, 19.8322, 3.2891998, -6.6893, 16.7232, 3.3913999, -6.6893, 19.8322, 3.2891998, -6.6893, 16.7232, 3.3913999, -10.033899, 19.798899, 3.3913999, -10.033899, 19.8322, 3.2891998, -6.6893, 19.798899, 3.3913999, -10.033899, 19.831999, 3.3902998, -10.0008, 20.198, 3.1490998, -10.033899, 19.831999, 3.3902998, -10.0008, 19.798899, 3.3913999, -10.033899, 20.198, 3.1490998, -10.033899, 20.1796, 3.182, -9.995999, 19.831999, 3.3902998, -10.0008, 20.144999, 2.7605999, -3.3446, 19.8322, 3.0491, -3.602, 20.1349, 2.7974, -3.3783998, 19.8322, 3.0491, -3.602, 20.144999, 2.7605999, -3.3446, 16.7127, 2.8734, -3.3446, 19.8322, 3.0491, -3.602, 16.7127, 2.8734, -3.3446, 16.7232, 3.1513, -3.602, 15.0508995, 0, 6.6893, 16.7232, 1.5715, 6.6893, 16.7232, 0, 6.6893, 16.7232, 0, 6.6893, 16.7232, 1.5715, 6.6893, 20.0679, -0.124199994, 6.6893, 15.0508995, 0, 6.6893, 13.5511, 3.1797, 6.6893, 16.7232, 1.5715, 6.6893, 15.0508995, 0, 6.6893, 13.4036, 3.0749998, 6.6893, 13.5511, 3.1797, 6.6893, 13.4036, 3.0749998, 6.6893, 15.0508995, 0, 6.6893, 13.8046, 0, 6.6893, -16.7232, 0.6137, 0, -15.7546, 0.6137, 3.5372, -16.7232, 0.6137, 2.9166, -15.7546, 0.6137, 3.5372, -16.7232, 0.6137, 0, -13.3786, 0.6137, 0, -16.7232, 0.6137, 0, -15.8036995, 0.6137, -2.7091, -13.3786, 0.6137, -3.3446, -16.7232, 0.6137, 0, -13.3786, 0.6137, -3.3446, -13.3786, 0.6137, 0, -13.3786, 0.6137, 0, -13.3786, 0.6137, -3.3446, -10.033899, 0.6137, -3.3446, -15.7546, 0.6137, 3.5372, -13.3786, 0.6137, 0, -14.4892, 0.6137, 4.2778, -13.3786, 0.6137, 0, -10.033899, 0.6137, -3.3446, -10.033899, 0.6137, 0, -14.4892, 0.6137, 4.2778, -13.3786, 0.6137, 0, -10.033899, 0.6137, 0, -10.033899, 0.6137, 0, -10.033899, 0.6137, -3.3446, -6.6893, 0.6137, -3.3446, -10.033899, 0.6137, 0, -6.6893, 0.6137, -3.3446, -6.6893, 0.6137, 0, -6.6893, 0.6137, 0, -6.6893, 0.6137, -3.3446, -4.0257998, 0.6137, -1.6911999, -6.6893, 0.6137, 0, -4.0257998, 0.6137, -1.6911999, -3.3446, 0.6137, 0, -10.033899, 0.6137, 3.3446, -10.033899, 0.6137, 0, -6.6893, 0.6137, 0, -14.4892, 0.6137, 4.2778, -10.033899, 0.6137, 0, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 0, -3.3446, 0.6137, 0, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 0, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -3.3446, 0.6137, 0, -3.3446, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -14.4892, 0.6137, 4.2778, -10.033899, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -10.033899, 0.6137, 3.3446, -6.6893, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -6.6893, 0.6137, 3.3446, -6.6893, 0.6137, 6.6893, -10.033899, 0.6137, 6.6893, -13.677099, 0.6137, 5.2616, -14.4892, 0.6137, 4.2778, -13.677099, 0.6137, 5.2616, -10.033899, 0.6137, 6.6893, -12.747499, 0.6137, 6.3671, -6.6893, 0.6137, 3.3446, -2.5674999, 0.0266, 7.6215, -6.6893, 0.6137, 6.6893, -2.5674999, 0.0266, 7.6215, -6.6893, 0.6137, 3.3446, -3.3446, 0.6137, 3.3446, -2.5674999, 0.0266, 7.6215, -3.3446, 0.6137, 3.3446, -2.1193, 0.6137, 5.2736998, -16.7232, 3.0033998, 2.9166, -15.7546, 0.6137, 3.5372, -15.372, 3.0033998, 3.6132998, -16.7232, 3.0033998, 2.9166, -16.7232, 0.6137, 2.9166, -15.7546, 0.6137, 3.5372, -14.4892, 3.0033998, 4.2778, -13.677099, 0.6137, 5.2616, -13.6942, 3.0033998, 5.1798, -14.4892, 3.0033998, 4.2778, -14.4892, 0.6137, 4.2778, -13.677099, 0.6137, 5.2616, -15.8036995, 3.0033998, -2.7091, -16.7232, 0.6137, 0, -16.7232, 3.0033998, 0, -15.8036995, 3.0033998, -2.7091, -15.8036995, 0.6137, -2.7091, -16.7232, 0.6137, 0, -16.7232, 3.0033998, 0, -16.7232, 0.6137, 2.9166, -16.7232, 3.0033998, 2.9166, -16.7232, 3.0033998, 0, -16.7232, 0.6137, 0, -16.7232, 0.6137, 2.9166, -3.3446, 3.0033998, 0, -4.0257998, 0.6137, -1.6911999, -4.0257998, 3.0033998, -1.6911999, -3.3446, 3.0033998, 0, -3.3446, 0.6137, 0, -4.0257998, 0.6137, -1.6911999, -3.3446, 3.0033998, 3.3446, -3.3446, 0.6137, 0, -3.3446, 3.0033998, 0, -3.3446, 3.0033998, 3.3446, -3.3446, 0.6137, 3.3446, -3.3446, 0.6137, 0, -12.747499, 3.0033998, 6.3671, -10.033899, 0.6137, 6.6893, -10.033899, 3.0033998, 6.6893, -12.747499, 3.0033998, 6.3671, -12.747499, 0.6137, 6.3671, -10.033899, 0.6137, 6.6893, -10.033899, 3.0033998, -3.3446, -13.3786, 0.6137, -3.3446, -13.3786, 3.0033998, -3.3446, -10.033899, 3.0033998, -3.3446, -10.033899, 0.6137, -3.3446, -13.3786, 0.6137, -3.3446, -6.6893, 3.0033998, -3.3446, -10.033899, 0.6137, -3.3446, -10.033899, 3.0033998, -3.3446, -6.6893, 3.0033998, -3.3446, -6.6893, 0.6137, -3.3446, -10.033899, 0.6137, -3.3446, -13.3786, 3.0033998, -3.3446, -15.8036995, 0.6137, -2.7091, -15.8036995, 3.0033998, -2.7091, -13.3786, 3.0033998, -3.3446, -13.3786, 0.6137, -3.3446, -15.8036995, 0.6137, -2.7091, -4.0257998, 3.0033998, -1.6911999, -6.6893, 0.6137, -3.3446, -6.6893, 3.0033998, -3.3446, -4.0257998, 3.0033998, -1.6911999, -4.0257998, 0.6137, -1.6911999, -6.6893, 0.6137, -3.3446, -6.6893, 3.0033998, 6.6893, -2.5674999, 0.0266, 7.6215, -2.5674999, 2.4164, 7.6215, -6.6893, 3.0033998, 6.6893, -6.6893, 0.6137, 6.6893, -2.5674999, 0.0266, 7.6215, -10.033899, 3.0033998, 6.6893, -6.6893, 0.6137, 6.6893, -6.6893, 3.0033998, 6.6893, -10.033899, 3.0033998, 6.6893, -10.033899, 0.6137, 6.6893, -6.6893, 0.6137, 6.6893, -2.5674999, 2.4164, 7.6215, -2.1193, 0.6137, 5.2736998, -2.1397, 3.0033998, 5.1795, -2.5674999, 2.4164, 7.6215, -2.5674999, 0.0266, 7.6215, -2.1193, 0.6137, 5.2736998, -2.1193, 0.6137, 5.2736998, -3.3446, 3.0033998, 3.3446, -2.1397, 3.0033998, 5.1795, -2.1193, 0.6137, 5.2736998, -3.3446, 0.6137, 3.3446, -3.3446, 3.0033998, 3.3446, -13.677099, 0.6137, 5.2616, -12.747499, 3.0033998, 6.3671, -13.6942, 3.0033998, 5.1798, -13.677099, 0.6137, 5.2616, -12.747499, 0.6137, 6.3671, -12.747499, 3.0033998, 6.3671, -15.7546, 0.6137, 3.5372, -14.4892, 3.0033998, 4.2778, -15.372, 3.0033998, 3.6132998, -15.7546, 0.6137, 3.5372, -14.4892, 0.6137, 4.2778, -14.4892, 3.0033998, 4.2778, 21.1831, 0, -13.6544, 16.8204, 3.138, -13.447599, 18.0241, 0, -14.3019, 21.1831, 0, -13.6544, 20.1539, 3.1299, -13.399799, 16.8204, 3.138, -13.447599, 21.8888, 0, -10.033899, 20.1539, 3.1299, -13.399799, 21.1831, 0, -13.6544, 21.8888, 0, -10.033899, 20.198, 3.1490998, -10.033899, 20.1539, 3.1299, -13.399799, 17.2285, 0, -16.8036, 12.7471, 3.1766999, -16.1988, 13.7014, 0, -17.416399, 17.2285, 0, -16.8036, 16.756899, 3.1650999, -16.7286, 12.7471, 3.1766999, -16.1988, 16.8204, 3.138, -13.447599, 17.2285, 0, -16.8036, 18.0241, 0, -14.3019, 17.2285, 0, -16.8036, 16.8204, 3.138, -13.447599, 16.8051, 3.1778, -13.479, 17.2285, 0, -16.8036, 16.8051, 3.1778, -13.479, 16.756899, 3.1650999, -16.7286, 12.7471, 3.1766999, -16.1988, 13.3786, 0, -20.5992, 13.7014, 0, -17.416399, 13.3786, 0, -20.5992, 12.7471, 3.1766999, -16.1988, 12.747, 3.1771, -16.1991, 13.3786, 0, -20.5992, 12.747, 3.1771, -16.1991, 12.723599, 2.9213, -19.2902, 13.3786, 0, -20.5992, 10.033899, 3.0523999, -19.0294, 10.033899, 0, -20.467, 13.3786, 0, -20.5992, 12.723599, 2.9213, -19.2902, 10.033899, 3.0523999, -19.0294, 10.033899, 0, -20.467, 6.6893, 3.0440998, -19.036999, 6.6893, 0, -20.1318, 10.033899, 0, -20.467, 10.033899, 3.0523999, -19.0294, 6.6893, 3.0440998, -19.036999, 3.0140998, 0, -16.7232, 4.4131, 3.2091, -13.357699, 3.3446, -0.3989, -12.9443, 3.0140998, 0, -16.7232, 3.5895, 3.2033, -16.7232, 4.4131, 3.2091, -13.357699, 6.6893, 3.0440998, -19.036999, 3.3446, 0, -20.5992, 6.6893, 0, -20.1318, 3.3446, 0, -20.5992, 6.6893, 3.0440998, -19.036999, 6.6640997, 3.0702, -19.018099, 3.3446, 0, -20.5992, 6.6640997, 3.0702, -19.018099, 4.4783998, 3.1067, -18.2665, 3.3446, 0, -20.5992, 3.5895, 3.2033, -16.7232, 3.0140998, 0, -16.7232, 3.3446, 0, -20.5992, 4.4783998, 3.1067, -18.2665, 3.5895, 3.2033, -16.7232, 2.8241, 0, -10.4594, 0, 3.179, -9.0652, 0, 0, -10.7654, 2.8241, 0, -10.4594, 3.7529, 3.1712, -9.0494995, 0, 3.179, -9.0652, 3.3446, -0.3989, -12.9443, 3.7529, 3.1712, -9.0494995, 2.8241, 0, -10.4594, 3.3446, -0.3989, -12.9443, 4.4131, 3.2091, -13.357699, 3.7529, 3.1712, -9.0494995, 4.4131, 3.2091, -13.357699, 3.3446, -0.3989, -12.9443, 4.4131, 3.2091, -13.357699, -2.6297, 0, -10.5745, -3.6538, 3.1585999, -12.813399, -3.3446, 0, -13.3786, -2.6297, 0, -10.5745, -3.6076, 3.1676, -10.0696, -3.6538, 3.1585999, -12.813399, -3.3446, 0, -13.3786, -6.6893, 3.175, -12.867, -6.6893, 0, -14.2637, -3.3446, 0, -13.3786, -3.6538, 3.1585999, -12.813399, -6.6893, 3.175, -12.867, 0, 3.179, -9.0652, -2.6297, 0, -10.5745, 0, 0, -10.7654, -2.6297, 0, -10.5745, 0, 3.179, -9.0652, -0.0081, 3.1864998, -9.063499, -2.6297, 0, -10.5745, -0.0081, 3.1864998, -9.063499, -3.6076, 3.1676, -10.0696, -6.6893, 0, -14.2637, -10.4043, 3.1608999, -13.4295, -9.6168995, 0, -14.127899, -6.6893, 0, -14.2637, -6.6893, 3.175, -12.867, -10.4043, 3.1608999, -13.4295, -6.6893, 3.175, -12.867, -6.6893, 0, -14.2637, -6.6893, 3.175, -12.867, -9.6168995, 0, -14.127899, -10.4319, 3.1557999, -16.1556, -10.033899, 0, -16.7232, -9.6168995, 0, -14.127899, -10.4043, 3.1608999, -13.4295, -10.4319, 3.1557999, -16.1556, -10.033899, 0, -16.7232, -13.3786, 3.1771998, -15.7625, -13.3786, 0, -17.3364, -10.033899, 0, -16.7232, -10.4319, 3.1557999, -16.1556, -13.3786, 3.1771998, -15.7625, -16.7232, 0, -16.7232, -16.3753, 3.4225, -13.6277, -16.7232, 0, -13.3786, -16.7232, 0, -16.7232, -16.0243, 3.3509998, -15.752, -16.3753, 3.4225, -13.6277, -13.3786, 3.1771998, -15.7625, -16.7232, 0, -16.7232, -13.3786, 0, -17.3364, -16.7232, 0, -16.7232, -13.3786, 3.1771998, -15.7625, -13.3911, 3.1892, -15.757, -16.7232, 0, -16.7232, -13.3911, 3.1892, -15.757, -16.0243, 3.3509998, -15.752, -17.275, 0, -10.5169, -19.6237, 3.1655998, -9.757899, -20.0679, 0, -10.033899, -17.275, 0, -10.5169, -16.4131, 3.1706, -9.788899, -19.6237, 3.1655998, -9.757899, -16.7232, 0, -13.3786, -16.4131, 3.1706, -9.788899, -17.275, 0, -10.5169, -16.7232, 0, -13.3786, -16.3753, 3.4225, -13.6277, -16.4131, 3.1706, -9.788899, -16.3753, 3.4225, -13.6277, -16.7232, 0, -13.3786, -16.3753, 3.4225, -13.6277, -20.3764, 0, -6.6893, -19.9489, 3.2063, -2.8082, -20.0679, -0.3484, -3.5669, -20.3764, 0, -6.6893, -19.515, 3.2122, -6.6893, -19.9489, 3.2063, -2.8082, -19.6237, 3.1655998, -9.757899, -20.3764, 0, -6.6893, -20.0679, 0, -10.033899, -20.3764, 0, -6.6893, -19.6237, 3.1655998, -9.757899, -19.6161, 3.2075999, -9.7116995, -20.3764, 0, -6.6893, -19.6161, 3.2075999, -9.7116995, -19.515, 3.2122, -6.6893, -23.412498, -0.2913, 0.26119998, -23.0809, 3.3083, 3.3615, -23.412498, 0, 3.3446, -23.412498, -0.2913, 0.26119998, -23.3676, 3.4459999, 0.1372, -23.0809, 3.3083, 3.3615, -23.412498, 0, -3.3446, -23.3676, 3.4459999, 0.1372, -23.412498, -0.2913, 0.26119998, -23.412498, 0, -3.3446, -23.390299, 3.3349998, -2.6938999, -23.3676, 3.4459999, 0.1372, -20.0679, -0.3484, -3.5669, -23.390299, 3.3349998, -2.6938999, -23.412498, 0, -3.3446, -20.0679, -0.3484, -3.5669, -19.9489, 3.2063, -2.8082, -23.390299, 3.3349998, -2.6938999, -19.9489, 3.2063, -2.8082, -20.0679, -0.3484, -3.5669, -19.9489, 3.2063, -2.8082, -23.0809, 3.3083, 3.3615, -23.8078, 0, 6.6893, -23.412498, 0, 3.3446, -23.8078, 0, 6.6893, -23.0809, 3.3083, 3.3615, -23.0777, 3.3197, 3.3734, -23.8078, 0, 6.6893, -23.0777, 3.3197, 3.3734, -22.563799, 3.2219, 6.6893, -23.8078, 0, 6.6893, -22.791399, 3.2124999, 9.4432, -23.412498, 0, 10.033899, -23.8078, 0, 6.6893, -22.563799, 3.2219, 6.6893, -22.791399, 3.2124999, 9.4432, -23.412498, 0, 10.033899, -20.0679, 3.1943, 9.407499, -20.0679, 0, 10.033899, -23.412498, 0, 10.033899, -22.791399, 3.2124999, 9.4432, -20.0679, 3.1943, 9.407499, -22.791399, 3.2124999, 9.4432, -23.412498, 0, 10.033899, -22.791399, 3.2124999, 9.4432, -20.0679, 0, 10.033899, -16.2818, 3.1913998, 9.8056, -16.7232, 0, 10.033899, -20.0679, 0, 10.033899, -20.0679, 3.1943, 9.407499, -16.2818, 3.1913998, 9.8056, -16.7232, 0, 13.3786, -13.3786, 3.194, 12.8089, -13.3786, 0, 13.3786, -16.7232, 0, 13.3786, -16.076399, 3.04, 12.735, -13.3786, 3.194, 12.8089, -16.7232, 0, 10.033899, -16.076399, 3.04, 12.735, -16.7232, 0, 13.3786, -16.7232, 0, 10.033899, -16.2818, 3.1913998, 9.8056, -16.076399, 3.04, 12.735, -16.2818, 3.1913998, 9.8056, -16.7232, 0, 10.033899, -16.2818, 3.1913998, 9.8056, -13.3786, 0, 13.3786, -10.033899, 2.9384, 12.4537, -10.033899, 0, 13.3786, -13.3786, 0, 13.3786, -13.3786, 3.194, 12.8089, -10.033899, 2.9384, 12.4537, -13.3786, 3.194, 12.8089, -13.3786, 0, 13.3786, -13.3786, 3.194, 12.8089, -10.033899, 0, 13.3786, -6.2748, 3.126, 13.122, -6.6893, 0, 13.3786, -10.033899, 0, 13.3786, -10.033899, 2.9384, 12.4537, -6.2748, 3.126, 13.122, -6.6893, 0, 13.3786, -6.2748, 3.1260998, 16.4145, -6.6893, 0, 16.7232, -6.6893, 0, 13.3786, -6.2748, 3.126, 13.122, -6.2748, 3.1260998, 16.4145, -3.3446, 0, 16.7232, 0, 2.9636998, 16.0179, 0, 0, 16.7232, -3.3446, 0, 16.7232, -3.3446, 3.1497998, 15.914599, 0, 2.9636998, 16.0179, 0, 0, 16.7232, 3.6425998, 3.1413999, 16.5127, 3.3446, 0, 16.7232, 0, 0, 16.7232, 0, 2.9636998, 16.0179, 3.6425998, 3.1413999, 16.5127, -6.2748, 3.1260998, 16.4145, -3.3446, 0, 16.7232, -6.6893, 0, 16.7232, -3.3446, 0, 16.7232, -6.2748, 3.1260998, 16.4145, -6.2558, 3.1427999, 16.4098, -3.3446, 0, 16.7232, -6.2558, 3.1427999, 16.4098, -3.3446, 3.1497998, 15.914599, 3.3446, 0, 20.0679, 6.6893, 3.2050998, 19.228699, 6.6893, 0, 20.0679, 3.3446, 0, 20.0679, 3.9266999, 3.5967, 19.6223, 6.6893, 3.2050998, 19.228699, 3.6425998, 3.1413999, 16.5127, 3.3446, 0, 20.0679, 3.3446, 0, 16.7232, 3.3446, 0, 20.0679, 3.6425998, 3.1413999, 16.5127, 3.6536, 3.2078998, 16.5669, 3.3446, 0, 20.0679, 3.6536, 3.2078998, 16.5669, 3.9266999, 3.5967, 19.6223, 6.6893, 3.2050998, 19.228699, 10.572599, 0, 20.0679, 6.6893, 0, 20.0679, 10.572599, 0, 20.0679, 6.6893, 3.2050998, 19.228699, 6.6991997, 3.2148, 19.2255, 10.572599, 0, 20.0679, 6.6991997, 3.2148, 19.2255, 9.3066, 3.2186, 19.0653, 10.572599, 0, 20.0679, 10.5913, 3.1985998, 16.7232, 10.572599, 0, 16.7232, 10.572599, 0, 20.0679, 9.3066, 3.2186, 19.0653, 10.5913, 3.1985998, 16.7232, 10.5913, 3.1985998, 16.7232, 10.572599, 0, 13.918799, 10.572599, 0, 16.7232, 10.572599, 0, 13.918799, 10.5913, 3.1985998, 16.7232, 10.5893, 3.2117, 16.7106, 10.572599, 0, 13.918799, 10.5893, 3.2117, 16.7106, 10.0546, 3.1885, 13.4071, 10.572599, 0, 13.918799, 13.2631, 3.5611, 13.4043, 13.8046, 0, 13.918799, 10.572599, 0, 13.918799, 10.0546, 3.1885, 13.4071, 13.2631, 3.5611, 13.4043, 13.8046, 0, 13.918799, 12.9758, 3.2036, 10.033899, 13.8046, 0, 10.033899, 13.8046, 0, 13.918799, 13.2631, 3.5611, 13.4043, 12.9758, 3.2036, 10.033899, 13.8046, 0, 13.918799, 13.2631, 3.5611, 13.4043, 13.2631, 3.5611, 13.4043, 13.8046, 0, 10.033899, 13.4036, 3.0749998, 6.6893, 13.8046, 0, 6.6893, 13.8046, 0, 10.033899, 12.9758, 3.2036, 10.033899, 13.4036, 3.0749998, 6.6893, 20.961199, 0, -3.3446, 16.7127, 2.8734, -3.3446, 20.144999, 2.7605999, -3.3446, 20.961199, 0, -3.3446, 14.0489, 3.0042, -3.3446, 16.7127, 2.8734, -3.3446, 20.144999, 2.7605999, -3.3446, 21.2349, 0, -6.6893, 20.961199, 0, -3.3446, 21.2349, 0, -6.6893, 20.144999, 2.7605999, -3.3446, 20.1349, 2.7974, -3.3783998, 21.2349, 0, -6.6893, 20.1349, 2.7974, -3.3783998, 20.147, 3.0588999, -6.6893, 20.147, 3.0588999, -6.6893, 21.8888, 0, -10.033899, 21.2349, 0, -6.6893, 21.8888, 0, -10.033899, 20.147, 3.0588999, -6.6893, 20.1796, 3.182, -9.995999, 21.8888, 0, -10.033899, 20.1796, 3.182, -9.995999, 20.198, 3.1490998, -10.033899, 23.412498, 0, -26.7572, 26.7572, -0.2593, -23.412498, 23.412498, 0, -23.412498, 26.7572, -0.2593, -23.412498, 23.412498, 0, -26.7572, 26.7572, -0.2593, -25.084799, 26.7572, -0.2593, -25.084799, 23.412498, 0, -26.7572, 26.7572, -0.2593, -26.7572, 26.7572, -0.2593, -21.7402, 23.412498, 0, -23.412498, 26.7572, -0.2593, -23.412498, 26.7572, -0.2593, -20.0679, 23.412498, 0, -23.412498, 26.7572, -0.2593, -21.7402, 23.412498, 0, -23.412498, 26.7572, -0.2593, -20.0679, 23.412498, 0, -20.0679, 26.7572, -0.2593, -18.3955, 23.412498, 0, -20.0679, 26.7572, -0.2593, -20.0679, 26.7572, -0.2593, -16.7232, 23.412498, 0, -20.0679, 26.7572, -0.2593, -18.3955, 23.412498, 0, -20.0679, 26.7572, -0.2593, -16.7232, 23.412498, 0, -16.7232, 26.7572, -0.2593, -15.0508995, 23.412498, 0, -16.7232, 26.7572, -0.2593, -16.7232, 26.7572, -0.2593, -13.3786, 23.412498, 0, -16.7232, 26.7572, -0.2593, -15.0508995, 23.412498, 0, -16.7232, 26.7572, -0.2593, -13.3786, 23.412498, 0, -13.3786, 26.7572, -0.2593, -11.7063, 23.412498, 0, -13.3786, 26.7572, -0.2593, -13.3786, 26.7572, -0.2593, -10.033899, 23.412498, 0, -13.3786, 26.7572, -0.2593, -11.7063, 23.412498, 0, -13.3786, 26.7572, -0.2593, -10.033899, 23.412498, 0, -10.033899, 26.7572, -0.2593, -8.3616, 23.412498, 0, -10.033899, 26.7572, -0.2593, -10.033899, 26.7572, -0.2593, -6.6893, 23.412498, 0, -10.033899, 26.7572, -0.2593, -8.3616, 23.412498, 0, -10.033899, 26.7572, -0.2593, -6.6893, 23.412498, 0, -6.6893, 26.7572, -0.2593, -5.0169997, 23.412498, 0, -6.6893, 26.7572, -0.2593, -6.6893, 26.7572, -0.2593, -3.3446, 23.412498, 0, -6.6893, 26.7572, -0.2593, -5.0169997, 23.412498, 0, -6.6893, 26.7572, -0.2593, -3.3446, 23.412498, 0, -3.3446, 26.7572, -0.2593, -1.6723, 23.412498, 0, -3.3446, 26.7572, -0.2593, -3.3446, 26.7572, -0.2593, 0, 23.412498, 0, -3.3446, 26.7572, -0.2593, -1.6723, 23.412498, 0, -3.3446, 26.7572, -0.2593, 0, 23.412498, 0, 0, 26.7572, -0.2593, 1.6723, 23.412498, 0, 0, 26.7572, -0.2593, 0, 26.7572, -0.2593, 3.3446, 23.412498, 0, 0, 26.7572, -0.2593, 1.6723, 23.412498, 0, 0, 26.7572, -0.2593, 3.3446, 23.412498, 0, 3.3446, 26.7572, -0.2593, 5.0169997, 23.412498, 0, 3.3446, 26.7572, -0.2593, 3.3446, 26.7572, -0.2593, 6.6893, 23.412498, 0, 3.3446, 26.7572, -0.2593, 5.0169997, 23.412498, 0, 3.3446, 26.7572, -0.2593, 6.6893, 23.412498, 0, 6.6893, 26.7572, -0.2593, 8.3616, 23.412498, 0, 6.6893, 26.7572, -0.2593, 6.6893, 26.7572, -0.2593, 10.033899, 23.412498, 0, 6.6893, 26.7572, -0.2593, 8.3616, 23.412498, 0, 6.6893, 26.7572, -0.2593, 10.033899, 23.412498, 0, 10.033899, 26.7572, -0.2593, 11.7063, 23.412498, 0, 10.033899, 26.7572, -0.2593, 10.033899, 26.7572, -0.2593, 13.3786, 23.412498, 0, 10.033899, 26.7572, -0.2593, 11.7063, 23.412498, 0, 10.033899, 26.7572, -0.2593, 13.3786, 23.412498, 0, 13.3786, 26.7572, -0.2593, 15.0508995, 23.412498, 0, 13.3786, 26.7572, -0.2593, 13.3786, 26.7572, -0.2593, 16.7232, 23.412498, 0, 13.3786, 26.7572, -0.2593, 15.0508995, 23.412498, 0, 13.3786, 26.7572, -0.2593, 16.7232, 23.412498, 0, 16.7232, 26.7572, -0.2593, 18.3955, 23.412498, 0, 16.7232, 26.7572, -0.2593, 16.7232, 26.7572, -0.2593, 20.0679, 23.412498, 0, 16.7232, 26.7572, -0.2593, 18.3955, 23.412498, 0, 16.7232, 26.7572, -0.2593, 20.0679, 23.412498, 0, 20.0679, 26.7572, -0.2593, 21.7402, 23.412498, 0, 20.0679, 26.7572, -0.2593, 20.0679, 26.7572, -0.2593, 23.412498, 23.412498, 0, 20.0679, 26.7572, -0.2593, 21.7402, 23.412498, 0, 20.0679, 26.7572, -0.2593, 23.412498, 23.412498, 0, 23.412498, 26.7572, -0.2593, 25.084799, 23.412498, 0, 23.412498, 26.7572, -0.2593, 23.412498, 26.7572, -0.2593, 26.7572, 23.412498, 0, 23.412498, 26.7572, -0.2593, 25.084799, 23.412498, 0, 23.412498, 26.7572, -0.2593, 26.7572, 23.412498, 0, 26.7572, -3.3446, 0, -23.412498, 0, 0, -26.7572, 0, 0, -23.412498, 0, 0, -23.412498, 0, 0, -26.7572, 3.3446, 0, -26.7572, -3.3446, 0, -23.412498, -3.3446, 0, -26.7572, 0, 0, -26.7572, 0, 0, -23.412498, 3.3446, 0, -26.7572, 3.3446, 0, -23.412498, 3.3446, 0, -23.412498, 3.3446, 0, -26.7572, 6.6893, 0, -26.7572, -3.3446, 0, -20.0679, -3.3446, 0, -23.412498, 0, 0, -23.412498, 3.3446, 0, -23.412498, 6.6893, 0, -26.7572, 6.6893, 0, -23.412498, 6.6893, 0, -23.412498, 6.6893, 0, -26.7572, 10.033899, 0, -26.7572, 0, 0, -20.0679, 0, 0, -23.412498, 3.3446, 0, -23.412498, -3.3446, 0, -20.0679, 0, 0, -23.412498, 0, 0, -20.0679, 3.3446, 0, -20.5992, 3.3446, 0, -23.412498, 6.6893, 0, -23.412498, 0, 0, -20.0679, 3.3446, 0, -23.412498, 3.3446, 0, -20.5992, 3.3446, 0, -20.5992, 6.6893, 0, -23.412498, 6.6893, 0, -20.1318, 6.6893, 0, -23.412498, 10.033899, 0, -26.7572, 10.033899, 0, -23.412498, 6.6893, 0, -20.1318, 6.6893, 0, -23.412498, 10.033899, 0, -23.412498, 10.033899, 0, -23.412498, 10.033899, 0, -26.7572, 13.3786, 0, -26.7572, 6.6893, 0, -20.1318, 10.033899, 0, -23.412498, 10.033899, 0, -20.467, 10.033899, 0, -23.412498, 13.3786, 0, -26.7572, 13.3786, 0, -23.412498, 10.033899, 0, -20.467, 10.033899, 0, -23.412498, 13.3786, 0, -23.412498, 13.3786, 0, -23.412498, 13.3786, 0, -26.7572, 16.7232, 0, -26.7572, 10.033899, 0, -20.467, 13.3786, 0, -23.412498, 13.3786, 0, -20.5992, 13.3786, 0, -23.412498, 16.7232, 0, -26.7572, 16.7232, 0, -23.412498, 13.3786, 0, -20.5992, 13.3786, 0, -23.412498, 16.7232, 0, -23.412498, 16.7232, 0, -23.412498, 16.7232, 0, -26.7572, 20.0679, 0, -26.7572, 13.3786, 0, -20.5992, 16.7232, 0, -23.412498, 16.7232, 0, -20.0679, 13.7014, 0, -17.416399, 13.3786, 0, -20.5992, 16.7232, 0, -20.0679, 13.7014, 0, -17.416399, 16.7232, 0, -20.0679, 17.2285, 0, -16.8036, 16.7232, 0, -23.412498, 20.0679, 0, -26.7572, 20.0679, 0, -23.412498, 16.7232, 0, -20.0679, 16.7232, 0, -23.412498, 20.0679, 0, -23.412498, 20.0679, 0, -23.412498, 20.0679, 0, -26.7572, 23.412498, 0, -26.7572, 20.0679, 0, -23.412498, 23.412498, 0, -26.7572, 23.412498, 0, -23.412498, 16.7232, 0, -20.0679, 20.0679, 0, -23.412498, 20.0679, 0, -20.0679, 20.0679, 0, -20.0679, 20.0679, 0, -23.412498, 23.412498, 0, -23.412498, 17.2285, 0, -16.8036, 16.7232, 0, -20.0679, 20.0679, 0, -20.0679, 20.0679, 0, -20.0679, 23.412498, 0, -23.412498, 23.412498, 0, -20.0679, 17.2285, 0, -16.8036, 20.0679, 0, -20.0679, 20.0679, 0, -16.7232, 20.0679, 0, -16.7232, 20.0679, 0, -20.0679, 23.412498, 0, -20.0679, 18.0241, 0, -14.3019, 17.2285, 0, -16.8036, 20.0679, 0, -16.7232, 20.0679, 0, -16.7232, 23.412498, 0, -20.0679, 23.412498, 0, -16.7232, 18.0241, 0, -14.3019, 20.0679, 0, -16.7232, 21.1831, 0, -13.6544, 21.1831, 0, -13.6544, 20.0679, 0, -16.7232, 23.412498, 0, -16.7232, 21.1831, 0, -13.6544, 23.412498, 0, -16.7232, 23.412498, 0, -13.3786, 21.8888, 0, -10.033899, 21.1831, 0, -13.6544, 23.412498, 0, -13.3786, 21.8888, 0, -10.033899, 23.412498, 0, -13.3786, 23.412498, 0, -10.033899, 21.2349, 0, -6.6893, 21.8888, 0, -10.033899, 23.412498, 0, -10.033899, 21.2349, 0, -6.6893, 23.412498, 0, -10.033899, 23.412498, 0, -6.6893, 20.961199, 0, -3.3446, 21.2349, 0, -6.6893, 23.412498, 0, -6.6893, 20.961199, 0, -3.3446, 23.412498, 0, -6.6893, 23.412498, 0, -3.3446, 20.0679, 0, 0, 20.961199, 0, -3.3446, 23.412498, 0, -3.3446, 20.0679, 0, 0, 23.412498, 0, -3.3446, 23.412498, 0, 0, 20.0679, 0, 3.3446, 20.0679, 0, 0, 23.412498, 0, 0, 20.0679, 0, 3.3446, 23.412498, 0, 0, 23.412498, 0, 3.3446, 10.572599, 0, 16.7232, 13.8046, 0, 13.918799, 13.3786, 0, 16.7232, 10.572599, 0, 16.7232, 10.572599, 0, 13.918799, 13.8046, 0, 13.918799, 10.572599, 0, 20.0679, 10.572599, 0, 16.7232, 13.3786, 0, 16.7232, 13.3786, 0, 16.7232, 13.8046, 0, 13.918799, 15.0508995, 0, 13.3786, 10.572599, 0, 20.0679, 13.3786, 0, 16.7232, 13.3786, 0, 20.0679, 13.8046, 0, 13.918799, 15.0508995, 0, 10.033899, 15.0508995, 0, 13.3786, 13.8046, 0, 13.918799, 13.8046, 0, 10.033899, 15.0508995, 0, 10.033899, 13.8046, 0, 10.033899, 15.0508995, 0, 6.6893, 15.0508995, 0, 10.033899, 13.8046, 0, 10.033899, 13.8046, 0, 6.6893, 15.0508995, 0, 6.6893, 15.0508995, 0, 10.033899, 15.0508995, 0, 6.6893, 16.7232, 0, 6.6893, 15.0508995, 0, 10.033899, 16.7232, 0, 6.6893, 16.7232, 0, 10.033899, 15.0508995, 0, 13.3786, 15.0508995, 0, 10.033899, 16.7232, 0, 10.033899, 13.3786, 0, 16.7232, 15.0508995, 0, 13.3786, 15.0508995, 0, 16.7232, 13.3786, 0, 20.0679, 13.3786, 0, 16.7232, 15.0508995, 0, 16.7232, 15.0508995, 0, 13.3786, 16.7232, 0, 10.033899, 16.7232, 0, 13.3786, 15.0508995, 0, 16.7232, 15.0508995, 0, 13.3786, 16.7232, 0, 13.3786, 16.7232, 0, 13.3786, 16.7232, 0, 10.033899, 20.0679, 0, 10.033899, 16.7232, 0, 13.3786, 20.0679, 0, 10.033899, 20.0679, 0, 13.3786, 20.0679, 0, 13.3786, 20.0679, 0, 10.033899, 23.412498, 0, 10.033899, 20.0679, 0, 13.3786, 23.412498, 0, 10.033899, 23.412498, 0, 13.3786, 16.7232, 0, 16.7232, 16.7232, 0, 13.3786, 20.0679, 0, 13.3786, 15.0508995, 0, 16.7232, 16.7232, 0, 13.3786, 16.7232, 0, 16.7232, 20.0679, 0, 16.7232, 20.0679, 0, 13.3786, 23.412498, 0, 13.3786, 16.7232, 0, 16.7232, 20.0679, 0, 13.3786, 20.0679, 0, 16.7232, 20.0679, 0, 16.7232, 23.412498, 0, 13.3786, 23.412498, 0, 16.7232, 15.0508995, 0, 20.0679, 15.0508995, 0, 16.7232, 16.7232, 0, 16.7232, 13.3786, 0, 20.0679, 15.0508995, 0, 16.7232, 15.0508995, 0, 20.0679, 16.7232, 0, 20.0679, 16.7232, 0, 16.7232, 20.0679, 0, 16.7232, 15.0508995, 0, 20.0679, 16.7232, 0, 16.7232, 16.7232, 0, 20.0679, 20.0679, 0, 20.0679, 20.0679, 0, 16.7232, 23.412498, 0, 16.7232, 16.7232, 0, 20.0679, 20.0679, 0, 16.7232, 20.0679, 0, 20.0679, 20.0679, 0, 20.0679, 23.412498, 0, 16.7232, 23.412498, 0, 20.0679, 20.0679, 0, 23.412498, 20.0679, 0, 20.0679, 23.412498, 0, 20.0679, 20.0679, 0, 23.412498, 23.412498, 0, 20.0679, 23.412498, 0, 23.412498, 20.0679, 0, 26.7572, 20.0679, 0, 23.412498, 23.412498, 0, 23.412498, 20.0679, 0, 26.7572, 23.412498, 0, 23.412498, 23.412498, 0, 26.7572, 16.7232, 0, 26.7572, 20.0679, 0, 23.412498, 20.0679, 0, 26.7572, 16.7232, 0, 23.412498, 20.0679, 0, 20.0679, 20.0679, 0, 23.412498, 16.7232, 0, 26.7572, 16.7232, 0, 23.412498, 20.0679, 0, 23.412498, 16.7232, 0, 23.412498, 16.7232, 0, 20.0679, 20.0679, 0, 20.0679, 15.0508995, 0, 26.7572, 16.7232, 0, 23.412498, 16.7232, 0, 26.7572, 15.0508995, 0, 23.412498, 16.7232, 0, 20.0679, 16.7232, 0, 23.412498, 15.0508995, 0, 26.7572, 15.0508995, 0, 23.412498, 16.7232, 0, 23.412498, 15.0508995, 0, 23.412498, 15.0508995, 0, 20.0679, 16.7232, 0, 20.0679, 13.3786, 0, 26.7572, 15.0508995, 0, 23.412498, 15.0508995, 0, 26.7572, 13.3786, 0, 23.412498, 15.0508995, 0, 20.0679, 15.0508995, 0, 23.412498, 13.3786, 0, 26.7572, 13.3786, 0, 23.412498, 15.0508995, 0, 23.412498, 13.3786, 0, 23.412498, 13.3786, 0, 20.0679, 15.0508995, 0, 20.0679, 10.033899, 0, 26.7572, 13.3786, 0, 23.412498, 13.3786, 0, 26.7572, 10.033899, 0, 23.412498, 13.3786, 0, 20.0679, 13.3786, 0, 23.412498, 10.033899, 0, 26.7572, 10.033899, 0, 23.412498, 13.3786, 0, 23.412498, 10.033899, 0, 23.412498, 10.572599, 0, 20.0679, 13.3786, 0, 20.0679, 6.6893, 0, 26.7572, 10.033899, 0, 23.412498, 10.033899, 0, 26.7572, 6.6893, 0, 23.412498, 10.572599, 0, 20.0679, 10.033899, 0, 23.412498, 6.6893, 0, 26.7572, 6.6893, 0, 23.412498, 10.033899, 0, 23.412498, 6.6893, 0, 23.412498, 6.6893, 0, 20.0679, 10.572599, 0, 20.0679, 3.3446, 0, 26.7572, 6.6893, 0, 23.412498, 6.6893, 0, 26.7572, 3.3446, 0, 23.412498, 6.6893, 0, 20.0679, 6.6893, 0, 23.412498, 3.3446, 0, 26.7572, 3.3446, 0, 23.412498, 6.6893, 0, 23.412498, 3.3446, 0, 23.412498, 3.3446, 0, 20.0679, 6.6893, 0, 20.0679, 0, 0, 26.7572, 3.3446, 0, 23.412498, 3.3446, 0, 26.7572, 0, 0, 26.7572, 0, 0, 23.412498, 3.3446, 0, 23.412498, 0, 0, 23.412498, 3.3446, 0, 20.0679, 3.3446, 0, 23.412498, -3.3446, 0, 26.7572, 0, 0, 23.412498, 0, 0, 26.7572, 0, 0, 23.412498, 0, 0, 20.0679, 3.3446, 0, 20.0679, 0, 0, 20.0679, 3.3446, 0, 16.7232, 3.3446, 0, 20.0679, 0, 0, 20.0679, 0, 0, 16.7232, 3.3446, 0, 16.7232, -3.3446, 0, 26.7572, -3.3446, 0, 23.412498, 0, 0, 23.412498, -3.3446, 0, 23.412498, 0, 0, 20.0679, 0, 0, 23.412498, -6.6893, 0, 26.7572, -3.3446, 0, 23.412498, -3.3446, 0, 26.7572, -3.3446, 0, 20.0679, 0, 0, 16.7232, 0, 0, 20.0679, -3.3446, 0, 23.412498, -3.3446, 0, 20.0679, 0, 0, 20.0679, -3.3446, 0, 20.0679, -3.3446, 0, 16.7232, 0, 0, 16.7232, -6.6893, 0, 26.7572, -6.6893, 0, 23.412498, -3.3446, 0, 23.412498, -6.6893, 0, 23.412498, -3.3446, 0, 20.0679, -3.3446, 0, 23.412498, -10.033899, 0, 26.7572, -6.6893, 0, 23.412498, -6.6893, 0, 26.7572, -6.6893, 0, 20.0679, -3.3446, 0, 16.7232, -3.3446, 0, 20.0679, -6.6893, 0, 23.412498, -6.6893, 0, 20.0679, -3.3446, 0, 20.0679, -6.6893, 0, 20.0679, -6.6893, 0, 16.7232, -3.3446, 0, 16.7232, -10.033899, 0, 26.7572, -10.033899, 0, 23.412498, -6.6893, 0, 23.412498, -10.033899, 0, 23.412498, -6.6893, 0, 20.0679, -6.6893, 0, 23.412498, -13.3786, 0, 26.7572, -10.033899, 0, 23.412498, -10.033899, 0, 26.7572, -10.033899, 0, 20.0679, -6.6893, 0, 16.7232, -6.6893, 0, 20.0679, -10.033899, 0, 23.412498, -10.033899, 0, 20.0679, -6.6893, 0, 20.0679, -13.3786, 0, 26.7572, -13.3786, 0, 23.412498, -10.033899, 0, 23.412498, -13.3786, 0, 23.412498, -10.033899, 0, 20.0679, -10.033899, 0, 23.412498, -16.7232, 0, 26.7572, -13.3786, 0, 23.412498, -13.3786, 0, 26.7572, -10.033899, 0, 20.0679, -10.033899, 0, 16.7232, -6.6893, 0, 16.7232, -10.033899, 0, 16.7232, -6.6893, 0, 13.3786, -6.6893, 0, 16.7232, -10.033899, 0, 16.7232, -10.033899, 0, 13.3786, -6.6893, 0, 13.3786, -13.3786, 0, 20.0679, -10.033899, 0, 16.7232, -10.033899, 0, 20.0679, -13.3786, 0, 23.412498, -13.3786, 0, 20.0679, -10.033899, 0, 20.0679, -13.3786, 0, 16.7232, -10.033899, 0, 13.3786, -10.033899, 0, 16.7232, -13.3786, 0, 20.0679, -13.3786, 0, 16.7232, -10.033899, 0, 16.7232, -13.3786, 0, 16.7232, -13.3786, 0, 13.3786, -10.033899, 0, 13.3786, -16.7232, 0, 26.7572, -16.7232, 0, 23.412498, -13.3786, 0, 23.412498, -16.7232, 0, 23.412498, -13.3786, 0, 20.0679, -13.3786, 0, 23.412498, -20.0679, 0, 26.7572, -16.7232, 0, 23.412498, -16.7232, 0, 26.7572, -16.7232, 0, 20.0679, -13.3786, 0, 16.7232, -13.3786, 0, 20.0679, -16.7232, 0, 23.412498, -16.7232, 0, 20.0679, -13.3786, 0, 20.0679, -16.7232, 0, 16.7232, -13.3786, 0, 13.3786, -13.3786, 0, 16.7232, -16.7232, 0, 20.0679, -16.7232, 0, 16.7232, -13.3786, 0, 16.7232, -16.7232, 0, 16.7232, -16.7232, 0, 13.3786, -13.3786, 0, 13.3786, -20.0679, 0, 26.7572, -20.0679, 0, 23.412498, -16.7232, 0, 23.412498, -20.0679, 0, 23.412498, -16.7232, 0, 20.0679, -16.7232, 0, 23.412498, -23.412498, 0, 26.7572, -20.0679, 0, 23.412498, -20.0679, 0, 26.7572, -20.0679, 0, 20.0679, -16.7232, 0, 16.7232, -16.7232, 0, 20.0679, -20.0679, 0, 23.412498, -20.0679, 0, 20.0679, -16.7232, 0, 20.0679, -23.412498, 0, 26.7572, -23.412498, 0, 23.412498, -20.0679, 0, 23.412498, -23.412498, 0, 23.412498, -20.0679, 0, 20.0679, -20.0679, 0, 23.412498, -26.7572, 0, 26.7572, -23.412498, 0, 23.412498, -23.412498, 0, 26.7572, -26.7572, 0, 26.7572, -26.7572, 0, 23.412498, -23.412498, 0, 23.412498, -26.7572, 0, 23.412498, -23.412498, 0, 20.0679, -23.412498, 0, 23.412498, -23.412498, 0, 23.412498, -23.412498, 0, 20.0679, -20.0679, 0, 20.0679, -26.7572, 0, 23.412498, -26.7572, 0, 20.0679, -23.412498, 0, 20.0679, -20.0679, 0, 20.0679, -20.0679, 0, 16.7232, -16.7232, 0, 16.7232, -23.412498, 0, 20.0679, -20.0679, 0, 16.7232, -20.0679, 0, 20.0679, -20.0679, 0, 16.7232, -16.7232, 0, 13.3786, -16.7232, 0, 16.7232, -26.7572, 0, 20.0679, -23.412498, 0, 16.7232, -23.412498, 0, 20.0679, -23.412498, 0, 20.0679, -23.412498, 0, 16.7232, -20.0679, 0, 16.7232, -26.7572, 0, 20.0679, -26.7572, 0, 16.7232, -23.412498, 0, 16.7232, -20.0679, 0, 16.7232, -20.0679, 0, 13.3786, -16.7232, 0, 13.3786, -23.412498, 0, 16.7232, -20.0679, 0, 13.3786, -20.0679, 0, 16.7232, -20.0679, 0, 13.3786, -16.7232, 0, 10.033899, -16.7232, 0, 13.3786, -20.0679, 0, 13.3786, -20.0679, 0, 10.033899, -16.7232, 0, 10.033899, -23.412498, 0, 16.7232, -23.412498, 0, 13.3786, -20.0679, 0, 13.3786, -23.412498, 0, 13.3786, -20.0679, 0, 10.033899, -20.0679, 0, 13.3786, -26.7572, 0, 16.7232, -23.412498, 0, 13.3786, -23.412498, 0, 16.7232, -23.412498, 0, 13.3786, -23.412498, 0, 10.033899, -20.0679, 0, 10.033899, -26.7572, 0, 16.7232, -26.7572, 0, 13.3786, -23.412498, 0, 13.3786, -26.7572, 0, 13.3786, -23.412498, 0, 10.033899, -23.412498, 0, 13.3786, -26.7572, 0, 13.3786, -26.7572, 0, 10.033899, -23.412498, 0, 10.033899, -26.7572, 0, 10.033899, -23.8078, 0, 6.6893, -23.412498, 0, 10.033899, -26.7572, 0, 10.033899, -26.7572, 0, 6.6893, -23.8078, 0, 6.6893, -26.7572, 0, 6.6893, -23.412498, 0, 3.3446, -23.8078, 0, 6.6893, -26.7572, 0, 6.6893, -26.7572, 0, 3.3446, -23.412498, 0, 3.3446, -16.7232, 0, -23.412498, -13.3786, 0, -26.7572, -13.3786, 0, -23.412498, -13.3786, 0, -23.412498, -13.3786, 0, -26.7572, -10.033899, 0, -26.7572, -16.7232, 0, -23.412498, -16.7232, 0, -26.7572, -13.3786, 0, -26.7572, -13.3786, 0, -23.412498, -10.033899, 0, -26.7572, -10.033899, 0, -23.412498, -10.033899, 0, -23.412498, -10.033899, 0, -26.7572, -6.6893, 0, -26.7572, -16.7232, 0, -20.0679, -16.7232, 0, -23.412498, -13.3786, 0, -23.412498, -10.033899, 0, -23.412498, -6.6893, 0, -26.7572, -6.6893, 0, -23.412498, -6.6893, 0, -23.412498, -6.6893, 0, -26.7572, -3.3446, 0, -26.7572, -6.6893, 0, -23.412498, -3.3446, 0, -26.7572, -3.3446, 0, -23.412498, -13.3786, 0, -20.0679, -13.3786, 0, -23.412498, -10.033899, 0, -23.412498, -16.7232, 0, -20.0679, -13.3786, 0, -23.412498, -13.3786, 0, -20.0679, -10.033899, 0, -20.0679, -10.033899, 0, -23.412498, -6.6893, 0, -23.412498, -13.3786, 0, -20.0679, -10.033899, 0, -23.412498, -10.033899, 0, -20.0679, -6.6893, 0, -20.0679, -6.6893, 0, -23.412498, -3.3446, 0, -23.412498, -10.033899, 0, -20.0679, -6.6893, 0, -23.412498, -6.6893, 0, -20.0679, -6.6893, 0, -20.0679, -3.3446, 0, -23.412498, -3.3446, 0, -20.0679, -13.3786, 0, -17.3364, -13.3786, 0, -20.0679, -10.033899, 0, -20.0679, -13.3786, 0, -17.3364, -10.033899, 0, -20.0679, -10.033899, 0, -16.7232, -10.033899, 0, -16.7232, -10.033899, 0, -20.0679, -6.6893, 0, -20.0679, -16.7232, 0, -16.7232, -13.3786, 0, -20.0679, -13.3786, 0, -17.3364, -16.7232, 0, -16.7232, -16.7232, 0, -20.0679, -13.3786, 0, -20.0679, -10.033899, 0, -16.7232, -6.6893, 0, -20.0679, -6.6893, 0, -16.7232, -6.6893, 0, -16.7232, -6.6893, 0, -20.0679, -3.3446, 0, -20.0679, -9.6168995, 0, -14.127899, -10.033899, 0, -16.7232, -6.6893, 0, -16.7232, -9.6168995, 0, -14.127899, -6.6893, 0, -16.7232, -6.6893, 0, -14.2637, -6.6893, 0, -16.7232, -3.3446, 0, -20.0679, -3.3446, 0, -16.7232, -6.6893, 0, -14.2637, -6.6893, 0, -16.7232, -3.3446, 0, -16.7232, -3.3446, 0, -16.7232, -3.3446, 0, -20.0679, 0, 0, -20.0679, -6.6893, 0, -14.2637, -3.3446, 0, -16.7232, -3.3446, 0, -13.3786, -3.3446, 0, -16.7232, 0, 0, -20.0679, 0, 0, -16.7232, -3.3446, 0, -13.3786, -3.3446, 0, -16.7232, 0, 0, -16.7232, 0, 0, -16.7232, 0, 0, -20.0679, 3.3446, 0, -20.5992, 0, 0, -16.7232, 3.3446, 0, -20.5992, 3.0140998, 0, -16.7232, -3.3446, 0, -13.3786, 0, 0, -16.7232, 0, 0, -13.3786, -2.6297, 0, -10.5745, -3.3446, 0, -13.3786, 0, 0, -13.3786, -2.6297, 0, -10.5745, 0, 0, -13.3786, 0, 0, -10.7654, -20.0679, 0, -16.7232, -16.7232, 0, -20.0679, -16.7232, 0, -16.7232, -20.0679, 0, -16.7232, -20.0679, 0, -20.0679, -16.7232, 0, -20.0679, -20.0679, 0, -20.0679, -16.7232, 0, -23.412498, -16.7232, 0, -20.0679, -20.0679, 0, -13.3786, -20.0679, 0, -16.7232, -16.7232, 0, -16.7232, -20.0679, 0, -13.3786, -16.7232, 0, -16.7232, -16.7232, 0, -13.3786, -23.412498, 0, -16.7232, -20.0679, 0, -20.0679, -20.0679, 0, -16.7232, -20.0679, 0, -10.033899, -20.0679, 0, -13.3786, -16.7232, 0, -13.3786, -20.0679, 0, -10.033899, -16.7232, 0, -13.3786, -17.275, 0, -10.5169, -23.412498, 0, -13.3786, -20.0679, 0, -16.7232, -20.0679, 0, -13.3786, -23.412498, 0, -13.3786, -23.412498, 0, -16.7232, -20.0679, 0, -16.7232, -23.412498, 0, -10.033899, -20.0679, 0, -13.3786, -20.0679, 0, -10.033899, -23.412498, 0, -10.033899, -23.412498, 0, -13.3786, -20.0679, 0, -13.3786, -26.7572, 0, -10.033899, -23.412498, 0, -13.3786, -23.412498, 0, -10.033899, -26.7572, 0, -10.033899, -26.7572, 0, -13.3786, -23.412498, 0, -13.3786, -26.7572, 0, -13.3786, -23.412498, 0, -16.7232, -23.412498, 0, -13.3786, -26.7572, 0, -6.6893, -26.7572, 0, -10.033899, -23.412498, 0, -10.033899, -26.7572, 0, -13.3786, -26.7572, 0, -16.7232, -23.412498, 0, -16.7232, -26.7572, 0, -6.6893, -23.412498, 0, -10.033899, -23.412498, 0, -6.6893, -23.412498, 0, -6.6893, -23.412498, 0, -10.033899, -20.0679, 0, -10.033899, -23.412498, 0, -6.6893, -20.0679, 0, -10.033899, -20.3764, 0, -6.6893, -26.7572, 0, -3.3446, -26.7572, 0, -6.6893, -23.412498, 0, -6.6893, -26.7572, 0, -3.3446, -23.412498, 0, -6.6893, -23.412498, 0, -3.3446, -26.7572, 0, -16.7232, -23.412498, 0, -20.0679, -23.412498, 0, -16.7232, -23.412498, 0, -16.7232, -23.412498, 0, -20.0679, -20.0679, 0, -20.0679, -26.7572, 0, -16.7232, -26.7572, 0, -20.0679, -23.412498, 0, -20.0679, -23.412498, 0, -20.0679, -20.0679, 0, -23.412498, -20.0679, 0, -20.0679, -20.0679, 0, -20.0679, -20.0679, 0, -23.412498, -16.7232, 0, -23.412498, -20.0679, 0, -23.412498, -16.7232, 0, -26.7572, -16.7232, 0, -23.412498, -20.0679, 0, -23.412498, -20.0679, 0, -26.7572, -16.7232, 0, -26.7572, -23.412498, 0, -20.0679, -23.412498, 0, -23.412498, -20.0679, 0, -23.412498, -23.412498, 0, -23.412498, -20.0679, 0, -26.7572, -20.0679, 0, -23.412498, -26.7572, 0, -20.0679, -23.412498, 0, -23.412498, -23.412498, 0, -20.0679, -23.412498, 0, -23.412498, -23.412498, 0, -26.7572, -20.0679, 0, -26.7572, -26.7572, 0, -20.0679, -26.7572, 0, -23.412498, -23.412498, 0, -23.412498, -26.7572, 0, -23.412498, -23.412498, 0, -26.7572, -23.412498, 0, -23.412498, -26.7572, 0, -23.412498, -26.7572, 0, -26.7572, -23.412498, 0, -26.7572, -23.412498, 0, -3.3446, -20.3764, 0, -6.6893, -20.0679, -0.3484, -3.5669, -23.412498, 0, -3.3446, -23.412498, 0, -6.6893, -20.3764, 0, -6.6893, 16.7232, 0, 10.033899, 20.0679, -0.124199994, 6.6893, 20.0679, 0, 10.033899, 16.7232, 0, 10.033899, 16.7232, 0, 6.6893, 20.0679, -0.124199994, 6.6893, 20.0679, 0, 10.033899, 23.412498, 0, 6.6893, 23.412498, 0, 10.033899, 20.0679, 0, 10.033899, 20.0679, -0.124199994, 6.6893, 23.412498, 0, 6.6893, -26.7572, 0, 3.3446, -23.412498, -0.2913, 0.26119998, -23.412498, 0, 3.3446, -26.7572, 0, 3.3446, -26.7572, 0, 0, -23.412498, -0.2913, 0.26119998, -26.7572, 0, 0, -23.412498, 0, -3.3446, -23.412498, -0.2913, 0.26119998, -26.7572, 0, 0, -26.7572, 0, -3.3446, -23.412498, 0, -3.3446, 0, 0, -13.3786, 3.0140998, 0, -16.7232, 3.3446, -0.3989, -12.9443, 0, 0, -13.3786, 0, 0, -16.7232, 3.0140998, 0, -16.7232, 0, 0, -10.7654, 3.3446, -0.3989, -12.9443, 2.8241, 0, -10.4594, 0, 0, -10.7654, 0, 0, -13.3786, 3.3446, -0.3989, -12.9443, 20.0679, -0.124199994, 6.6893, 23.412498, 0, 3.3446, 23.412498, 0, 6.6893, 20.0679, -0.124199994, 6.6893, 20.0679, 0, 3.3446, 23.412498, 0, 3.3446, 51.2184, -3.9413, -15.0508995, 54.072197, -3.9413, -13.3786, 51.2184, -3.9413, -13.3786, 51.2184, -3.9413, -13.3786, 54.072197, -3.9413, -13.3786, 54.072197, -3.9413, -11.7063, 51.2184, -3.9413, -15.0508995, 54.072197, -3.9413, -15.0508995, 54.072197, -3.9413, -13.3786, 51.2184, -3.9413, -13.3786, 54.072197, -3.9413, -11.7063, 51.2184, -3.9413, -11.7063, 51.2184, -3.9413, -11.7063, 54.072197, -3.9413, -11.7063, 54.072197, -3.9413, -10.033899, 48.3645, -3.9413, -15.0508995, 51.2184, -3.9413, -15.0508995, 51.2184, -3.9413, -13.3786, 51.2184, -3.9413, -11.7063, 54.072197, -3.9413, -10.033899, 51.2184, -3.9413, -10.033899, 51.2184, -3.9413, -10.033899, 54.072197, -3.9413, -10.033899, 54.072197, -3.9413, -8.3616, 48.3645, -3.9413, -13.3786, 51.2184, -3.9413, -13.3786, 51.2184, -3.9413, -11.7063, 48.3645, -3.9413, -15.0508995, 51.2184, -3.9413, -13.3786, 48.3645, -3.9413, -13.3786, 48.3645, -3.9413, -11.7063, 51.2184, -3.9413, -11.7063, 51.2184, -3.9413, -10.033899, 48.3645, -3.9413, -13.3786, 51.2184, -3.9413, -11.7063, 48.3645, -3.9413, -11.7063, 51.2184, -3.9413, -10.033899, 54.072197, -3.9413, -8.3616, 51.2184, -3.9413, -8.3616, 51.2184, -3.9413, -8.3616, 54.072197, -3.9413, -8.3616, 54.072197, -3.9413, -6.6893, 48.3645, -3.9413, -11.7063, 51.2184, -3.9413, -10.033899, 48.3645, -3.9413, -10.033899, 48.3645, -3.9413, -10.033899, 51.2184, -3.9413, -10.033899, 51.2184, -3.9413, -8.3616, 45.5107, -3.9413, -13.3786, 48.3645, -3.9413, -13.3786, 48.3645, -3.9413, -11.7063, 45.5107, -3.9413, -11.7063, 48.3645, -3.9413, -11.7063, 48.3645, -3.9413, -10.033899, 45.5107, -3.9413, -13.3786, 48.3645, -3.9413, -11.7063, 45.5107, -3.9413, -11.7063, 51.2184, -3.9413, -8.3616, 54.072197, -3.9413, -6.6893, 51.2184, -3.9413, -6.6893, 51.2184, -3.9413, -6.6893, 54.072197, -3.9413, -6.6893, 54.072197, -3.9413, -5.0169997, 48.3645, -3.9413, -10.033899, 51.2184, -3.9413, -8.3616, 48.3645, -3.9413, -8.3616, 48.3645, -3.9413, -8.3616, 51.2184, -3.9413, -8.3616, 51.2184, -3.9413, -6.6893, 45.5107, -3.9413, -11.7063, 48.3645, -3.9413, -10.033899, 45.5107, -3.9413, -10.033899, 45.5107, -3.9413, -10.033899, 48.3645, -3.9413, -10.033899, 48.3645, -3.9413, -8.3616, 51.2184, -3.9413, -6.6893, 54.072197, -3.9413, -5.0169997, 51.2184, -3.9413, -5.0169997, 51.2184, -3.9413, -5.0169997, 54.072197, -3.9413, -5.0169997, 54.072197, -3.9413, -3.3446, 48.3645, -3.9413, -8.3616, 51.2184, -3.9413, -6.6893, 48.3645, -3.9413, -6.6893, 48.3645, -3.9413, -6.6893, 51.2184, -3.9413, -6.6893, 51.2184, -3.9413, -5.0169997, 45.5107, -3.9413, -10.033899, 48.3645, -3.9413, -8.3616, 45.5107, -3.9413, -8.3616, 45.5107, -3.9413, -8.3616, 48.3645, -3.9413, -8.3616, 48.3645, -3.9413, -6.6893, 51.2184, -3.9413, -5.0169997, 54.072197, -3.9413, -3.3446, 51.2184, -3.9413, -3.3446, 51.2184, -3.9413, -3.3446, 54.072197, -3.9413, -3.3446, 54.072197, -3.9413, -1.6723, 48.3645, -3.9413, -6.6893, 51.2184, -3.9413, -5.0169997, 48.3645, -3.9413, -5.0169997, 48.3645, -3.9413, -5.0169997, 51.2184, -3.9413, -5.0169997, 51.2184, -3.9413, -3.3446, 45.5107, -3.9413, -8.3616, 48.3645, -3.9413, -6.6893, 45.5107, -3.9413, -6.6893, 45.5107, -3.9413, -6.6893, 48.3645, -3.9413, -6.6893, 48.3645, -3.9413, -5.0169997, 51.2184, -3.9413, -3.3446, 54.072197, -3.9413, -1.6723, 51.2184, -3.9413, -1.6723, 51.2184, -3.9413, -1.6723, 54.072197, -3.9413, -1.6723, 54.072197, -3.9413, 0, 48.3645, -3.9413, -5.0169997, 51.2184, -3.9413, -3.3446, 48.3645, -3.9413, -3.3446, 48.3645, -3.9413, -3.3446, 51.2184, -3.9413, -3.3446, 51.2184, -3.9413, -1.6723, 45.5107, -3.9413, -6.6893, 48.3645, -3.9413, -5.0169997, 45.5107, -3.9413, -5.0169997, 45.5107, -3.9413, -5.0169997, 48.3645, -3.9413, -5.0169997, 48.3645, -3.9413, -3.3446, 51.2184, -3.9413, -1.6723, 54.072197, -3.9413, 0, 51.2184, -3.9413, 0, 51.2184, -3.9413, 0, 54.072197, -3.9413, 0, 54.072197, -3.9413, 1.6723, 48.3645, -3.9413, -3.3446, 51.2184, -3.9413, -1.6723, 48.3645, -3.9413, -1.6723, 48.3645, -3.9413, -1.6723, 51.2184, -3.9413, -1.6723, 51.2184, -3.9413, 0, 45.5107, -3.9413, -5.0169997, 48.3645, -3.9413, -3.3446, 45.5107, -3.9413, -3.3446, 45.5107, -3.9413, -3.3446, 48.3645, -3.9413, -3.3446, 48.3645, -3.9413, -1.6723, 51.2184, -3.9413, 0, 54.072197, -3.9413, 1.6723, 51.2184, -3.9413, 1.6723, 51.2184, -3.9413, 1.6723, 54.072197, -3.9413, 1.6723, 54.072197, -3.9413, 3.3446, 48.3645, -3.9413, -1.6723, 51.2184, -3.9413, 0, 48.3645, -3.9413, 0, 48.3645, -3.9413, 0, 51.2184, -3.9413, 0, 51.2184, -3.9413, 1.6723, 45.5107, -3.9413, -3.3446, 48.3645, -3.9413, -1.6723, 45.5107, -3.9413, -1.6723, 45.5107, -3.9413, -1.6723, 48.3645, -3.9413, -1.6723, 48.3645, -3.9413, 0, 51.2184, -3.9413, 1.6723, 54.072197, -3.9413, 3.3446, 51.2184, -3.9413, 3.3446, 51.2184, -3.9413, 3.3446, 54.072197, -3.9413, 3.3446, 54.072197, -3.9413, 5.0169997, 48.3645, -3.9413, 0, 51.2184, -3.9413, 1.6723, 48.3645, -3.9413, 1.6723, 48.3645, -3.9413, 1.6723, 51.2184, -3.9413, 1.6723, 51.2184, -3.9413, 3.3446, 45.5107, -3.9413, -1.6723, 48.3645, -3.9413, 0, 45.5107, -3.9413, 0, 45.5107, -3.9413, 0, 48.3645, -3.9413, 0, 48.3645, -3.9413, 1.6723, 51.2184, -3.9413, 3.3446, 54.072197, -3.9413, 5.0169997, 51.2184, -3.9413, 5.0169997, 51.2184, -3.9413, 5.0169997, 54.072197, -3.9413, 5.0169997, 54.072197, -3.9413, 6.6893, 48.3645, -3.9413, 1.6723, 51.2184, -3.9413, 3.3446, 48.3645, -3.9413, 3.3446, 48.3645, -3.9413, 3.3446, 51.2184, -3.9413, 3.3446, 51.2184, -3.9413, 5.0169997, 45.5107, -3.9413, 0, 48.3645, -3.9413, 1.6723, 45.5107, -3.9413, 1.6723, 45.5107, -3.9413, 1.6723, 48.3645, -3.9413, 1.6723, 48.3645, -3.9413, 3.3446, 51.2184, -3.9413, 5.0169997, 54.072197, -3.9413, 6.6893, 51.2184, -3.9413, 6.6893, 51.2184, -3.9413, 6.6893, 54.072197, -3.9413, 6.6893, 54.072197, -3.9413, 8.3616, 48.3645, -3.9413, 3.3446, 51.2184, -3.9413, 5.0169997, 48.3645, -3.9413, 5.0169997, 48.3645, -3.9413, 5.0169997, 51.2184, -3.9413, 5.0169997, 51.2184, -3.9413, 6.6893, 45.5107, -3.9413, 1.6723, 48.3645, -3.9413, 3.3446, 45.5107, -3.9413, 3.3446, 45.5107, -3.9413, 3.3446, 48.3645, -3.9413, 3.3446, 48.3645, -3.9413, 5.0169997, 51.2184, -3.9413, 6.6893, 54.072197, -3.9413, 8.3616, 51.2184, -3.9413, 8.3616, 51.2184, -3.9413, 8.3616, 54.072197, -3.9413, 8.3616, 54.072197, -3.9413, 10.033899, 48.3645, -3.9413, 5.0169997, 51.2184, -3.9413, 6.6893, 48.3645, -3.9413, 6.6893, 48.3645, -3.9413, 6.6893, 51.2184, -3.9413, 6.6893, 51.2184, -3.9413, 8.3616, 45.5107, -3.9413, 3.3446, 48.3645, -3.9413, 5.0169997, 45.5107, -3.9413, 5.0169997, 45.5107, -3.9413, 5.0169997, 48.3645, -3.9413, 5.0169997, 48.3645, -3.9413, 6.6893, 51.2184, -3.9413, 8.3616, 54.072197, -3.9413, 10.033899, 51.2184, -3.9413, 10.033899, 51.2184, -3.9413, 10.033899, 54.072197, -3.9413, 10.033899, 54.072197, -3.9413, 11.7063, 48.3645, -3.9413, 6.6893, 51.2184, -3.9413, 8.3616, 48.3645, -3.9413, 8.3616, 48.3645, -3.9413, 8.3616, 51.2184, -3.9413, 8.3616, 51.2184, -3.9413, 10.033899, 45.5107, -3.9413, 5.0169997, 48.3645, -3.9413, 6.6893, 45.5107, -3.9413, 6.6893, 45.5107, -3.9413, 6.6893, 48.3645, -3.9413, 6.6893, 48.3645, -3.9413, 8.3616, 51.2184, -3.9413, 10.033899, 54.072197, -3.9413, 11.7063, 51.2184, -3.9413, 11.7063, 51.2184, -3.9413, 11.7063, 54.072197, -3.9413, 11.7063, 54.072197, -3.9413, 13.3786, 48.3645, -3.9413, 8.3616, 51.2184, -3.9413, 10.033899, 48.3645, -3.9413, 10.033899, 48.3645, -3.9413, 10.033899, 51.2184, -3.9413, 10.033899, 51.2184, -3.9413, 11.7063, 45.5107, -3.9413, 6.6893, 48.3645, -3.9413, 8.3616, 45.5107, -3.9413, 8.3616, 45.5107, -3.9413, 8.3616, 48.3645, -3.9413, 8.3616, 48.3645, -3.9413, 10.033899, 51.2184, -3.9413, 11.7063, 54.072197, -3.9413, 13.3786, 51.2184, -3.9413, 13.3786, 51.2184, -3.9413, 13.3786, 54.072197, -3.9413, 13.3786, 54.072197, -3.9413, 15.0508995, 48.3645, -3.9413, 10.033899, 51.2184, -3.9413, 11.7063, 48.3645, -3.9413, 11.7063, 48.3645, -3.9413, 11.7063, 51.2184, -3.9413, 11.7063, 51.2184, -3.9413, 13.3786, 45.5107, -3.9413, 8.3616, 48.3645, -3.9413, 10.033899, 45.5107, -3.9413, 10.033899, 45.5107, -3.9413, 10.033899, 48.3645, -3.9413, 10.033899, 48.3645, -3.9413, 11.7063, 51.2184, -3.9413, 13.3786, 54.072197, -3.9413, 15.0508995, 51.2184, -3.9413, 15.0508995, 51.2184, -3.9413, 15.0508995, 54.072197, -3.9413, 15.0508995, 54.072197, -3.9413, 16.7232, 48.3645, -3.9413, 11.7063, 51.2184, -3.9413, 13.3786, 48.3645, -3.9413, 13.3786, 48.3645, -3.9413, 13.3786, 51.2184, -3.9413, 13.3786, 51.2184, -3.9413, 15.0508995, 45.5107, -3.9413, 10.033899, 48.3645, -3.9413, 11.7063, 45.5107, -3.9413, 11.7063, 45.5107, -3.9413, 11.7063, 48.3645, -3.9413, 11.7063, 48.3645, -3.9413, 13.3786, 51.2184, -3.9413, 15.0508995, 54.072197, -3.9413, 16.7232, 51.2184, -3.9413, 16.7232, 51.2184, -3.9413, 16.7232, 54.072197, -3.9413, 16.7232, 54.072197, -3.9413, 18.3955, 48.3645, -3.9413, 13.3786, 51.2184, -3.9413, 15.0508995, 48.3645, -3.9413, 15.0508995, 48.3645, -3.9413, 15.0508995, 51.2184, -3.9413, 15.0508995, 51.2184, -3.9413, 16.7232, 45.5107, -3.9413, 11.7063, 48.3645, -3.9413, 13.3786, 45.5107, -3.9413, 13.3786, 45.5107, -3.9413, 13.3786, 48.3645, -3.9413, 13.3786, 48.3645, -3.9413, 15.0508995, 51.2184, -3.9413, 16.7232, 54.072197, -3.9413, 18.3955, 51.2184, -3.9413, 18.3955, 51.2184, -3.9413, 18.3955, 54.072197, -3.9413, 18.3955, 54.072197, -3.9413, 20.0679, 48.3645, -3.9413, 15.0508995, 51.2184, -3.9413, 16.7232, 48.3645, -3.9413, 16.7232, 48.3645, -3.9413, 16.7232, 51.2184, -3.9413, 16.7232, 51.2184, -3.9413, 18.3955, 45.5107, -3.9413, 13.3786, 48.3645, -3.9413, 15.0508995, 45.5107, -3.9413, 15.0508995, 45.5107, -3.9413, 15.0508995, 48.3645, -3.9413, 15.0508995, 48.3645, -3.9413, 16.7232, 51.2184, -3.9413, 18.3955, 54.072197, -3.9413, 20.0679, 51.2184, -3.9413, 20.0679, 51.2184, -3.9413, 20.0679, 54.072197, -3.9413, 20.0679, 54.072197, -3.9413, 21.7402, 48.3645, -3.9413, 16.7232, 51.2184, -3.9413, 18.3955, 48.3645, -3.9413, 18.3955, 48.3645, -3.9413, 18.3955, 51.2184, -3.9413, 18.3955, 51.2184, -3.9413, 20.0679, 45.5107, -3.9413, 15.0508995, 48.3645, -3.9413, 16.7232, 45.5107, -3.9413, 16.7232, 45.5107, -3.9413, 16.7232, 48.3645, -3.9413, 16.7232, 48.3645, -3.9413, 18.3955, 51.2184, -3.9413, 20.0679, 54.072197, -3.9413, 21.7402, 51.2184, -3.9413, 21.7402, 51.2184, -3.9413, 21.7402, 54.072197, -3.9413, 21.7402, 54.072197, -3.9413, 23.412498, 48.3645, -3.9413, 18.3955, 51.2184, -3.9413, 20.0679, 48.3645, -3.9413, 20.0679, 48.3645, -3.9413, 20.0679, 51.2184, -3.9413, 20.0679, 51.2184, -3.9413, 21.7402, 45.5107, -3.9413, 16.7232, 48.3645, -3.9413, 18.3955, 45.5107, -3.9413, 18.3955, 45.5107, -3.9413, 18.3955, 48.3645, -3.9413, 18.3955, 48.3645, -3.9413, 20.0679, 51.2184, -3.9413, 21.7402, 54.072197, -3.9413, 23.412498, 51.2184, -3.9413, 23.412498, 51.2184, -3.9413, 23.412498, 54.072197, -3.9413, 23.412498, 54.072197, -3.9413, 25.084799, 48.3645, -3.9413, 20.0679, 51.2184, -3.9413, 21.7402, 48.3645, -3.9413, 21.7402, 48.3645, -3.9413, 21.7402, 51.2184, -3.9413, 21.7402, 51.2184, -3.9413, 23.412498, 45.5107, -3.9413, 18.3955, 48.3645, -3.9413, 20.0679, 45.5107, -3.9413, 20.0679, 45.5107, -3.9413, 20.0679, 48.3645, -3.9413, 20.0679, 48.3645, -3.9413, 21.7402, 51.2184, -3.9413, 23.412498, 54.072197, -3.9413, 25.084799, 51.2184, -3.9413, 25.084799, 51.2184, -3.9413, 25.084799, 54.072197, -3.9413, 25.084799, 54.072197, -3.9413, 26.7572, 51.2184, -3.9413, 25.084799, 54.072197, -3.9413, 26.7572, 51.2184, -3.9413, 26.7572, 48.3645, -3.9413, 23.412498, 51.2184, -3.9413, 23.412498, 51.2184, -3.9413, 25.084799, 48.3645, -3.9413, 21.7402, 51.2184, -3.9413, 23.412498, 48.3645, -3.9413, 23.412498, 48.3645, -3.9413, 25.084799, 51.2184, -3.9413, 25.084799, 51.2184, -3.9413, 26.7572, 48.3645, -3.9413, 23.412498, 51.2184, -3.9413, 25.084799, 48.3645, -3.9413, 25.084799, 48.3645, -3.9413, 25.084799, 51.2184, -3.9413, 26.7572, 48.3645, -3.9413, 26.7572, 45.5107, -3.9413, 21.7402, 48.3645, -3.9413, 21.7402, 48.3645, -3.9413, 23.412498, 45.5107, -3.9413, 20.0679, 48.3645, -3.9413, 21.7402, 45.5107, -3.9413, 21.7402, 45.5107, -3.9413, 23.412498, 48.3645, -3.9413, 23.412498, 48.3645, -3.9413, 25.084799, 45.5107, -3.9413, 21.7402, 48.3645, -3.9413, 23.412498, 45.5107, -3.9413, 23.412498, 45.5107, -3.9413, 25.084799, 48.3645, -3.9413, 25.084799, 48.3645, -3.9413, 26.7572, 45.5107, -3.9413, 23.412498, 48.3645, -3.9413, 25.084799, 45.5107, -3.9413, 25.084799, 45.5107, -3.9413, 25.084799, 48.3645, -3.9413, 26.7572, 45.5107, -3.9413, 26.7572, 42.6568, -3.9413, 25.084799, 45.5107, -3.9413, 25.084799, 45.5107, -3.9413, 26.7572, 42.6568, -3.9413, 25.084799, 45.5107, -3.9413, 26.7572, 42.6568, -3.9413, 26.7572, 39.7973, -3.9413, 25.084799, 42.6568, -3.9413, 25.084799, 42.6568, -3.9413, 26.7572, 39.7973, -3.9413, 25.084799, 42.6568, -3.9413, 26.7572, 39.7973, -3.9413, 26.7572, 39.7973, -3.9413, 23.412498, 42.6568, -3.9413, 25.084799, 39.7973, -3.9413, 25.084799, 42.6568, -3.9413, 23.412498, 45.5107, -3.9413, 25.084799, 42.6568, -3.9413, 25.084799, 39.7973, -3.9413, 23.412498, 42.6568, -3.9413, 23.412498, 42.6568, -3.9413, 25.084799, 42.6568, -3.9413, 23.412498, 45.5107, -3.9413, 23.412498, 45.5107, -3.9413, 25.084799, 39.7973, -3.9413, 21.7402, 42.6568, -3.9413, 23.412498, 39.7973, -3.9413, 23.412498, 42.6568, -3.9413, 21.7402, 45.5107, -3.9413, 23.412498, 42.6568, -3.9413, 23.412498, 39.7973, -3.9413, 21.7402, 42.6568, -3.9413, 21.7402, 42.6568, -3.9413, 23.412498, 42.6568, -3.9413, 21.7402, 45.5107, -3.9413, 21.7402, 45.5107, -3.9413, 23.412498, 39.7973, -3.9413, 20.0679, 42.6568, -3.9413, 21.7402, 39.7973, -3.9413, 21.7402, 42.6568, -3.9413, 20.0679, 45.5107, -3.9413, 21.7402, 42.6568, -3.9413, 21.7402, 39.7973, -3.9413, 20.0679, 42.6568, -3.9413, 20.0679, 42.6568, -3.9413, 21.7402, 42.6568, -3.9413, 20.0679, 45.5107, -3.9413, 20.0679, 45.5107, -3.9413, 21.7402, 39.7973, -3.9413, 18.3955, 42.6568, -3.9413, 20.0679, 39.7973, -3.9413, 20.0679, 42.6568, -3.9413, 18.3955, 45.5107, -3.9413, 20.0679, 42.6568, -3.9413, 20.0679, 39.7973, -3.9413, 18.3955, 42.6568, -3.9413, 18.3955, 42.6568, -3.9413, 20.0679, 42.6568, -3.9413, 18.3955, 45.5107, -3.9413, 18.3955, 45.5107, -3.9413, 20.0679, 39.7973, -3.9413, 16.7232, 42.6568, -3.9413, 18.3955, 39.7973, -3.9413, 18.3955, 42.6568, -3.9413, 16.7232, 45.5107, -3.9413, 18.3955, 42.6568, -3.9413, 18.3955, 39.7973, -3.9413, 16.7232, 42.6568, -3.9413, 16.7232, 42.6568, -3.9413, 18.3955, 42.6568, -3.9413, 16.7232, 45.5107, -3.9413, 16.7232, 45.5107, -3.9413, 18.3955, 39.7973, -3.9413, 15.0508995, 42.6568, -3.9413, 16.7232, 39.7973, -3.9413, 16.7232, 42.6568, -3.9413, 15.0508995, 45.5107, -3.9413, 16.7232, 42.6568, -3.9413, 16.7232, 39.7973, -3.9413, 15.0508995, 42.6568, -3.9413, 15.0508995, 42.6568, -3.9413, 16.7232, 42.6568, -3.9413, 15.0508995, 45.5107, -3.9413, 15.0508995, 45.5107, -3.9413, 16.7232, 39.7973, -3.9413, 13.3786, 42.6568, -3.9413, 15.0508995, 39.7973, -3.9413, 15.0508995, 42.6568, -3.9413, 13.3786, 45.5107, -3.9413, 15.0508995, 42.6568, -3.9413, 15.0508995, 39.7973, -3.9413, 13.3786, 42.6568, -3.9413, 13.3786, 42.6568, -3.9413, 15.0508995, 42.6568, -3.9413, 13.3786, 45.5107, -3.9413, 13.3786, 45.5107, -3.9413, 15.0508995, 39.7973, -3.9413, 11.7063, 42.6568, -3.9413, 13.3786, 39.7973, -3.9413, 13.3786, 42.6568, -3.9413, 11.7063, 45.5107, -3.9413, 13.3786, 42.6568, -3.9413, 13.3786, 39.7973, -3.9413, 11.7063, 42.6568, -3.9413, 11.7063, 42.6568, -3.9413, 13.3786, 42.6568, -3.9413, 11.7063, 45.5107, -3.9413, 11.7063, 45.5107, -3.9413, 13.3786, 39.7973, -3.9413, 10.033899, 42.6568, -3.9413, 11.7063, 39.7973, -3.9413, 11.7063, 42.6568, -3.9413, 10.033899, 45.5107, -3.9413, 11.7063, 42.6568, -3.9413, 11.7063, 39.7973, -3.9413, 10.033899, 42.6568, -3.9413, 10.033899, 42.6568, -3.9413, 11.7063, 42.6568, -3.9413, 10.033899, 45.5107, -3.9413, 10.033899, 45.5107, -3.9413, 11.7063, 39.7973, -3.9413, 8.3616, 42.6568, -3.9413, 10.033899, 39.7973, -3.9413, 10.033899, 42.6568, -3.9413, 8.3616, 45.5107, -3.9413, 10.033899, 42.6568, -3.9413, 10.033899, 39.7973, -3.9413, 8.3616, 42.6568, -3.9413, 8.3616, 42.6568, -3.9413, 10.033899, 42.6568, -3.9413, 8.3616, 45.5107, -3.9413, 8.3616, 45.5107, -3.9413, 10.033899, 39.7973, -3.9413, 6.6893, 42.6568, -3.9413, 8.3616, 39.7973, -3.9413, 8.3616, 42.6568, -3.9413, 6.6893, 45.5107, -3.9413, 8.3616, 42.6568, -3.9413, 8.3616, 39.7973, -3.9413, 6.6893, 42.6568, -3.9413, 6.6893, 42.6568, -3.9413, 8.3616, 42.6568, -3.9413, 6.6893, 45.5107, -3.9413, 6.6893, 45.5107, -3.9413, 8.3616, 39.7973, -3.9413, 5.0169997, 42.6568, -3.9413, 6.6893, 39.7973, -3.9413, 6.6893, 42.6568, -3.9413, 5.0169997, 45.5107, -3.9413, 6.6893, 42.6568, -3.9413, 6.6893, 39.7973, -3.9413, 5.0169997, 42.6568, -3.9413, 5.0169997, 42.6568, -3.9413, 6.6893, 42.6568, -3.9413, 5.0169997, 45.5107, -3.9413, 5.0169997, 45.5107, -3.9413, 6.6893, 39.7973, -3.9413, 3.3446, 42.6568, -3.9413, 5.0169997, 39.7973, -3.9413, 5.0169997, 42.6568, -3.9413, 3.3446, 45.5107, -3.9413, 5.0169997, 42.6568, -3.9413, 5.0169997, 39.7973, -3.9413, 3.3446, 42.6568, -3.9413, 3.3446, 42.6568, -3.9413, 5.0169997, 42.6568, -3.9413, 3.3446, 45.5107, -3.9413, 3.3446, 45.5107, -3.9413, 5.0169997, 39.7973, -3.9413, 1.6723, 42.6568, -3.9413, 3.3446, 39.7973, -3.9413, 3.3446, 42.6568, -3.9413, 1.6723, 45.5107, -3.9413, 3.3446, 42.6568, -3.9413, 3.3446, 39.7973, -3.9413, 1.6723, 42.6568, -3.9413, 1.6723, 42.6568, -3.9413, 3.3446, 42.6568, -3.9413, 1.6723, 45.5107, -3.9413, 1.6723, 45.5107, -3.9413, 3.3446, 39.7973, -3.9413, 0, 42.6568, -3.9413, 1.6723, 39.7973, -3.9413, 1.6723, 42.6568, -3.9413, 0, 45.5107, -3.9413, 1.6723, 42.6568, -3.9413, 1.6723, 39.7973, -3.9413, 0, 42.6568, -3.9413, 0, 42.6568, -3.9413, 1.6723, 42.6568, -3.9413, 0, 45.5107, -3.9413, 0, 45.5107, -3.9413, 1.6723, 39.7973, -3.9413, -1.6723, 42.6568, -3.9413, 0, 39.7973, -3.9413, 0, 42.6568, -3.9413, -1.6723, 45.5107, -3.9413, 0, 42.6568, -3.9413, 0, 39.7973, -3.9413, -1.6723, 42.6568, -3.9413, -1.6723, 42.6568, -3.9413, 0, 42.6568, -3.9413, -1.6723, 45.5107, -3.9413, -1.6723, 45.5107, -3.9413, 0, 39.7973, -3.9413, -3.3446, 42.6568, -3.9413, -1.6723, 39.7973, -3.9413, -1.6723, 42.6568, -3.9413, -3.3446, 45.5107, -3.9413, -1.6723, 42.6568, -3.9413, -1.6723, 39.7973, -3.9413, -3.3446, 42.6568, -3.9413, -3.3446, 42.6568, -3.9413, -1.6723, 42.6568, -3.9413, -3.3446, 45.5107, -3.9413, -3.3446, 45.5107, -3.9413, -1.6723, 39.7973, -3.9413, -5.0169997, 42.6568, -3.9413, -3.3446, 39.7973, -3.9413, -3.3446, 42.6568, -3.9413, -5.0169997, 45.5107, -3.9413, -3.3446, 42.6568, -3.9413, -3.3446, 39.7973, -3.9413, -5.0169997, 42.6568, -3.9413, -5.0169997, 42.6568, -3.9413, -3.3446, 42.6568, -3.9413, -5.0169997, 45.5107, -3.9413, -5.0169997, 45.5107, -3.9413, -3.3446, 39.7973, -3.9413, -6.6893, 42.6568, -3.9413, -5.0169997, 39.7973, -3.9413, -5.0169997, 42.6568, -3.9413, -6.6893, 45.5107, -3.9413, -5.0169997, 42.6568, -3.9413, -5.0169997, 39.7973, -3.9413, -6.6893, 42.6568, -3.9413, -6.6893, 42.6568, -3.9413, -5.0169997, 42.6568, -3.9413, -6.6893, 45.5107, -3.9413, -6.6893, 45.5107, -3.9413, -5.0169997, 39.7973, -3.9413, -8.3616, 42.6568, -3.9413, -6.6893, 39.7973, -3.9413, -6.6893, 42.6568, -3.9413, -8.3616, 45.5107, -3.9413, -6.6893, 42.6568, -3.9413, -6.6893, 39.7973, -3.9413, -8.3616, 42.6568, -3.9413, -8.3616, 42.6568, -3.9413, -6.6893, 42.6568, -3.9413, -8.3616, 45.5107, -3.9413, -8.3616, 45.5107, -3.9413, -6.6893, 39.7973, -3.9413, -10.033899, 42.6568, -3.9413, -8.3616, 39.7973, -3.9413, -8.3616, 42.6568, -3.9413, -10.033899, 45.5107, -3.9413, -8.3616, 42.6568, -3.9413, -8.3616, 39.7973, -3.9413, -10.033899, 42.6568, -3.9413, -10.033899, 42.6568, -3.9413, -8.3616, 42.6568, -3.9413, -10.033899, 45.5107, -3.9413, -10.033899, 45.5107, -3.9413, -8.3616, 39.7973, -3.9413, -11.7063, 42.6568, -3.9413, -10.033899, 39.7973, -3.9413, -10.033899, 42.6568, -3.9413, -11.7063, 45.5107, -3.9413, -10.033899, 42.6568, -3.9413, -10.033899, 39.7973, -3.9413, -11.7063, 42.6568, -3.9413, -11.7063, 42.6568, -3.9413, -10.033899, 42.6568, -3.9413, -11.7063, 45.5107, -3.9413, -11.7063, 45.5107, -3.9413, -10.033899, 39.7973, -3.9413, -13.3786, 42.6568, -3.9413, -11.7063, 39.7973, -3.9413, -11.7063, 42.6568, -3.9413, -13.3786, 45.5107, -3.9413, -11.7063, 42.6568, -3.9413, -11.7063, 39.7973, -3.9413, -13.3786, 42.6568, -3.9413, -13.3786, 42.6568, -3.9413, -11.7063, 42.6568, -3.9413, -13.3786, 45.5107, -3.9413, -13.3786, 45.5107, -3.9413, -11.7063, 39.7973, -3.9413, -15.0508995, 42.6568, -3.9413, -13.3786, 39.7973, -3.9413, -13.3786, 39.7973, -3.9413, -15.0508995, 42.6568, -3.9413, -15.0508995, 42.6568, -3.9413, -13.3786, 42.6568, -3.9413, -15.0508995, 45.5107, -3.9413, -13.3786, 42.6568, -3.9413, -13.3786, 39.7973, -3.9413, -16.7232, 42.6568, -3.9413, -15.0508995, 39.7973, -3.9413, -15.0508995, 42.6568, -3.9413, -15.0508995, 45.5107, -3.9413, -15.0508995, 45.5107, -3.9413, -13.3786, 45.5107, -3.9413, -15.0508995, 48.3645, -3.9413, -13.3786, 45.5107, -3.9413, -13.3786, 39.7973, -3.9413, -16.7232, 42.6568, -3.9413, -16.7232, 42.6568, -3.9413, -15.0508995, 42.6568, -3.9413, -16.7232, 45.5107, -3.9413, -15.0508995, 42.6568, -3.9413, -15.0508995, 39.7973, -3.9413, -18.3955, 42.6568, -3.9413, -16.7232, 39.7973, -3.9413, -16.7232, 45.5107, -3.9413, -15.0508995, 48.3645, -3.9413, -15.0508995, 48.3645, -3.9413, -13.3786, 39.7973, -3.9413, -18.3955, 42.6568, -3.9413, -18.3955, 42.6568, -3.9413, -16.7232, 42.6568, -3.9413, -16.7232, 45.5107, -3.9413, -16.7232, 45.5107, -3.9413, -15.0508995, 45.5107, -3.9413, -16.7232, 48.3645, -3.9413, -15.0508995, 45.5107, -3.9413, -15.0508995, 42.6568, -3.9413, -18.3955, 45.5107, -3.9413, -16.7232, 42.6568, -3.9413, -16.7232, 39.7973, -3.9413, -20.0679, 42.6568, -3.9413, -18.3955, 39.7973, -3.9413, -18.3955, 45.5107, -3.9413, -16.7232, 48.3645, -3.9413, -16.7232, 48.3645, -3.9413, -15.0508995, 48.3645, -3.9413, -16.7232, 51.2184, -3.9413, -15.0508995, 48.3645, -3.9413, -15.0508995, 42.6568, -3.9413, -18.3955, 45.5107, -3.9413, -18.3955, 45.5107, -3.9413, -16.7232, 45.5107, -3.9413, -18.3955, 48.3645, -3.9413, -16.7232, 45.5107, -3.9413, -16.7232, 39.7973, -3.9413, -20.0679, 42.6568, -3.9413, -20.0679, 42.6568, -3.9413, -18.3955, 42.6568, -3.9413, -20.0679, 45.5107, -3.9413, -18.3955, 42.6568, -3.9413, -18.3955, 39.7973, -3.9413, -21.7402, 42.6568, -3.9413, -20.0679, 39.7973, -3.9413, -20.0679, 48.3645, -3.9413, -16.7232, 51.2184, -3.9413, -16.7232, 51.2184, -3.9413, -15.0508995, 51.2184, -3.9413, -16.7232, 54.072197, -3.9413, -15.0508995, 51.2184, -3.9413, -15.0508995, 51.2184, -3.9413, -16.7232, 54.072197, -3.9413, -16.7232, 54.072197, -3.9413, -15.0508995, 48.3645, -3.9413, -18.3955, 51.2184, -3.9413, -16.7232, 48.3645, -3.9413, -16.7232, 45.5107, -3.9413, -18.3955, 48.3645, -3.9413, -18.3955, 48.3645, -3.9413, -16.7232, 51.2184, -3.9413, -18.3955, 54.072197, -3.9413, -16.7232, 51.2184, -3.9413, -16.7232, 48.3645, -3.9413, -18.3955, 51.2184, -3.9413, -18.3955, 51.2184, -3.9413, -16.7232, 51.2184, -3.9413, -18.3955, 54.072197, -3.9413, -18.3955, 54.072197, -3.9413, -16.7232, 45.5107, -3.9413, -20.0679, 48.3645, -3.9413, -18.3955, 45.5107, -3.9413, -18.3955, 42.6568, -3.9413, -20.0679, 45.5107, -3.9413, -20.0679, 45.5107, -3.9413, -18.3955, 48.3645, -3.9413, -20.0679, 51.2184, -3.9413, -18.3955, 48.3645, -3.9413, -18.3955, 45.5107, -3.9413, -20.0679, 48.3645, -3.9413, -20.0679, 48.3645, -3.9413, -18.3955, 51.2184, -3.9413, -20.0679, 54.072197, -3.9413, -18.3955, 51.2184, -3.9413, -18.3955, 48.3645, -3.9413, -20.0679, 51.2184, -3.9413, -20.0679, 51.2184, -3.9413, -18.3955, 51.2184, -3.9413, -20.0679, 54.072197, -3.9413, -20.0679, 54.072197, -3.9413, -18.3955, 39.7973, -3.9413, -21.7402, 42.6568, -3.9413, -21.7402, 42.6568, -3.9413, -20.0679, 42.6568, -3.9413, -21.7402, 45.5107, -3.9413, -20.0679, 42.6568, -3.9413, -20.0679, 39.7973, -3.9413, -23.412498, 42.6568, -3.9413, -21.7402, 39.7973, -3.9413, -21.7402, 45.5107, -3.9413, -21.7402, 48.3645, -3.9413, -20.0679, 45.5107, -3.9413, -20.0679, 42.6568, -3.9413, -21.7402, 45.5107, -3.9413, -21.7402, 45.5107, -3.9413, -20.0679, 39.7973, -3.9413, -23.412498, 42.6568, -3.9413, -23.412498, 42.6568, -3.9413, -21.7402, 42.6568, -3.9413, -23.412498, 45.5107, -3.9413, -21.7402, 42.6568, -3.9413, -21.7402, 39.7973, -3.9413, -25.084799, 42.6568, -3.9413, -23.412498, 39.7973, -3.9413, -23.412498, 45.5107, -3.9413, -21.7402, 48.3645, -3.9413, -21.7402, 48.3645, -3.9413, -20.0679, 48.3645, -3.9413, -21.7402, 51.2184, -3.9413, -20.0679, 48.3645, -3.9413, -20.0679, 42.6568, -3.9413, -23.412498, 45.5107, -3.9413, -23.412498, 45.5107, -3.9413, -21.7402, 45.5107, -3.9413, -23.412498, 48.3645, -3.9413, -21.7402, 45.5107, -3.9413, -21.7402, 39.7973, -3.9413, -25.084799, 42.6568, -3.9413, -25.084799, 42.6568, -3.9413, -23.412498, 42.6568, -3.9413, -25.084799, 45.5107, -3.9413, -23.412498, 42.6568, -3.9413, -23.412498, 39.7973, -3.9413, -26.7572, 42.6568, -3.9413, -25.084799, 39.7973, -3.9413, -25.084799, 39.7973, -3.9413, -26.7572, 42.6568, -3.9413, -26.7572, 42.6568, -3.9413, -25.084799, 42.6568, -3.9413, -26.7572, 45.5107, -3.9413, -25.084799, 42.6568, -3.9413, -25.084799, 42.6568, -3.9413, -25.084799, 45.5107, -3.9413, -25.084799, 45.5107, -3.9413, -23.412498, 42.6568, -3.9413, -26.7572, 45.5107, -3.9413, -26.7572, 45.5107, -3.9413, -25.084799, 45.5107, -3.9413, -23.412498, 48.3645, -3.9413, -23.412498, 48.3645, -3.9413, -21.7402, 45.5107, -3.9413, -25.084799, 48.3645, -3.9413, -23.412498, 45.5107, -3.9413, -23.412498, 45.5107, -3.9413, -26.7572, 48.3645, -3.9413, -25.084799, 45.5107, -3.9413, -25.084799, 45.5107, -3.9413, -25.084799, 48.3645, -3.9413, -25.084799, 48.3645, -3.9413, -23.412498, 45.5107, -3.9413, -26.7572, 48.3645, -3.9413, -26.7572, 48.3645, -3.9413, -25.084799, 48.3645, -3.9413, -21.7402, 51.2184, -3.9413, -21.7402, 51.2184, -3.9413, -20.0679, 48.3645, -3.9413, -23.412498, 51.2184, -3.9413, -21.7402, 48.3645, -3.9413, -21.7402, 51.2184, -3.9413, -21.7402, 54.072197, -3.9413, -20.0679, 51.2184, -3.9413, -20.0679, 51.2184, -3.9413, -21.7402, 54.072197, -3.9413, -21.7402, 54.072197, -3.9413, -20.0679, 48.3645, -3.9413, -23.412498, 51.2184, -3.9413, -23.412498, 51.2184, -3.9413, -21.7402, 51.2184, -3.9413, -23.412498, 54.072197, -3.9413, -21.7402, 51.2184, -3.9413, -21.7402, 48.3645, -3.9413, -25.084799, 51.2184, -3.9413, -23.412498, 48.3645, -3.9413, -23.412498, 51.2184, -3.9413, -23.412498, 54.072197, -3.9413, -23.412498, 54.072197, -3.9413, -21.7402, 48.3645, -3.9413, -26.7572, 51.2184, -3.9413, -25.084799, 48.3645, -3.9413, -25.084799, 48.3645, -3.9413, -25.084799, 51.2184, -3.9413, -25.084799, 51.2184, -3.9413, -23.412498, 51.2184, -3.9413, -25.084799, 54.072197, -3.9413, -23.412498, 51.2184, -3.9413, -23.412498, 48.3645, -3.9413, -26.7572, 51.2184, -3.9413, -26.7572, 51.2184, -3.9413, -25.084799, 51.2184, -3.9413, -25.084799, 54.072197, -3.9413, -25.084799, 54.072197, -3.9413, -23.412498, 51.2184, -3.9413, -26.7572, 54.072197, -3.9413, -25.084799, 51.2184, -3.9413, -25.084799, 51.2184, -3.9413, -26.7572, 54.072197, -3.9413, -26.7572, 54.072197, -3.9413, -25.084799, 29.679499, -0.6044, -15.0508995, 32.4649, -1.1213, -13.3786, 29.679499, -0.6044, -13.3786, 29.679499, -0.6044, -13.3786, 32.4649, -1.1213, -13.3786, 32.4649, -1.1213, -11.7063, 29.679499, -0.6044, -15.0508995, 32.4649, -1.1213, -15.0508995, 32.4649, -1.1213, -13.3786, 29.679499, -0.6044, -13.3786, 32.4649, -1.1213, -11.7063, 29.679499, -0.6044, -11.7063, 29.679499, -0.6044, -16.7232, 32.4649, -1.1213, -15.0508995, 29.679499, -0.6044, -15.0508995, 29.679499, -0.6044, -11.7063, 32.4649, -1.1213, -11.7063, 32.4649, -1.1213, -10.033899, 29.679499, -0.6044, -16.7232, 32.4649, -1.1213, -16.7232, 32.4649, -1.1213, -15.0508995, 29.679499, -0.6044, -11.7063, 32.4649, -1.1213, -10.033899, 29.679499, -0.6044, -10.033899, 29.679499, -0.6044, -18.3955, 32.4649, -1.1213, -16.7232, 29.679499, -0.6044, -16.7232, 29.679499, -0.6044, -10.033899, 32.4649, -1.1213, -10.033899, 32.4649, -1.1213, -8.3616, 29.679499, -0.6044, -18.3955, 32.4649, -1.1213, -18.3955, 32.4649, -1.1213, -16.7232, 29.679499, -0.6044, -10.033899, 32.4649, -1.1213, -8.3616, 29.679499, -0.6044, -8.3616, 29.679499, -0.6044, -20.0679, 32.4649, -1.1213, -18.3955, 29.679499, -0.6044, -18.3955, 29.679499, -0.6044, -8.3616, 32.4649, -1.1213, -8.3616, 32.4649, -1.1213, -6.6893, 29.679499, -0.6044, -20.0679, 32.4649, -1.1213, -20.0679, 32.4649, -1.1213, -18.3955, 29.679499, -0.6044, -8.3616, 32.4649, -1.1213, -6.6893, 29.679499, -0.6044, -6.6893, 29.679499, -0.6044, -21.7402, 32.4649, -1.1213, -20.0679, 29.679499, -0.6044, -20.0679, 29.679499, -0.6044, -6.6893, 32.4649, -1.1213, -6.6893, 32.4649, -1.1213, -5.0169997, 29.679499, -0.6044, -21.7402, 32.4649, -1.1213, -21.7402, 32.4649, -1.1213, -20.0679, 29.679499, -0.6044, -6.6893, 32.4649, -1.1213, -5.0169997, 29.679499, -0.6044, -5.0169997, 29.679499, -0.6044, -23.412498, 32.4649, -1.1213, -21.7402, 29.679499, -0.6044, -21.7402, 29.679499, -0.6044, -5.0169997, 32.4649, -1.1213, -5.0169997, 32.4649, -1.1213, -3.3446, 29.679499, -0.6044, -23.412498, 32.4649, -1.1213, -23.412498, 32.4649, -1.1213, -21.7402, 29.679499, -0.6044, -5.0169997, 32.4649, -1.1213, -3.3446, 29.679499, -0.6044, -3.3446, 29.679499, -0.6044, -25.084799, 32.4649, -1.1213, -23.412498, 29.679499, -0.6044, -23.412498, 29.679499, -0.6044, -3.3446, 32.4649, -1.1213, -3.3446, 32.4649, -1.1213, -1.6723, 29.679499, -0.6044, -25.084799, 32.4649, -1.1213, -25.084799, 32.4649, -1.1213, -23.412498, 29.679499, -0.6044, -26.7572, 32.4649, -1.1213, -25.084799, 29.679499, -0.6044, -25.084799, 29.679499, -0.6044, -26.7572, 32.4649, -1.1213, -26.7572, 32.4649, -1.1213, -25.084799, 29.679499, -0.6044, -3.3446, 32.4649, -1.1213, -1.6723, 29.679499, -0.6044, -1.6723, 29.679499, -0.6044, -1.6723, 32.4649, -1.1213, -1.6723, 32.4649, -1.1213, 0, 29.679499, -0.6044, -1.6723, 32.4649, -1.1213, 0, 29.679499, -0.6044, 0, 29.679499, -0.6044, 0, 32.4649, -1.1213, 0, 32.4649, -1.1213, 1.6723, 29.679499, -0.6044, 0, 32.4649, -1.1213, 1.6723, 29.679499, -0.6044, 1.6723, 29.679499, -0.6044, 1.6723, 32.4649, -1.1213, 1.6723, 32.4649, -1.1213, 3.3446, 29.679499, -0.6044, 1.6723, 32.4649, -1.1213, 3.3446, 29.679499, -0.6044, 3.3446, 29.679499, -0.6044, 3.3446, 32.4649, -1.1213, 3.3446, 32.4649, -1.1213, 5.0169997, 29.679499, -0.6044, 3.3446, 32.4649, -1.1213, 5.0169997, 29.679499, -0.6044, 5.0169997, 29.679499, -0.6044, 5.0169997, 32.4649, -1.1213, 5.0169997, 32.4649, -1.1213, 6.6893, 29.679499, -0.6044, 5.0169997, 32.4649, -1.1213, 6.6893, 29.679499, -0.6044, 6.6893, 29.679499, -0.6044, 6.6893, 32.4649, -1.1213, 6.6893, 32.4649, -1.1213, 8.3616, 29.679499, -0.6044, 6.6893, 32.4649, -1.1213, 8.3616, 29.679499, -0.6044, 8.3616, 29.679499, -0.6044, 8.3616, 32.4649, -1.1213, 8.3616, 32.4649, -1.1213, 10.033899, 29.679499, -0.6044, 8.3616, 32.4649, -1.1213, 10.033899, 29.679499, -0.6044, 10.033899, 29.679499, -0.6044, 10.033899, 32.4649, -1.1213, 10.033899, 32.4649, -1.1213, 11.7063, 29.679499, -0.6044, 10.033899, 32.4649, -1.1213, 11.7063, 29.679499, -0.6044, 11.7063, 29.679499, -0.6044, 11.7063, 32.4649, -1.1213, 11.7063, 32.4649, -1.1213, 13.3786, 29.679499, -0.6044, 11.7063, 32.4649, -1.1213, 13.3786, 29.679499, -0.6044, 13.3786, 29.679499, -0.6044, 13.3786, 32.4649, -1.1213, 13.3786, 32.4649, -1.1213, 15.0508995, 29.679499, -0.6044, 13.3786, 32.4649, -1.1213, 15.0508995, 29.679499, -0.6044, 15.0508995, 29.679499, -0.6044, 15.0508995, 32.4649, -1.1213, 15.0508995, 32.4649, -1.1213, 16.7232, 29.679499, -0.6044, 15.0508995, 32.4649, -1.1213, 16.7232, 29.679499, -0.6044, 16.7232, 29.679499, -0.6044, 16.7232, 32.4649, -1.1213, 16.7232, 32.4649, -1.1213, 18.3955, 29.679499, -0.6044, 16.7232, 32.4649, -1.1213, 18.3955, 29.679499, -0.6044, 18.3955, 29.679499, -0.6044, 18.3955, 32.4649, -1.1213, 18.3955, 32.4649, -1.1213, 20.0679, 29.679499, -0.6044, 18.3955, 32.4649, -1.1213, 20.0679, 29.679499, -0.6044, 20.0679, 29.679499, -0.6044, 20.0679, 32.4649, -1.1213, 20.0679, 32.4649, -1.1213, 21.7402, 29.679499, -0.6044, 20.0679, 32.4649, -1.1213, 21.7402, 29.679499, -0.6044, 21.7402, 29.679499, -0.6044, 21.7402, 32.4649, -1.1213, 21.7402, 32.4649, -1.1213, 23.412498, 29.679499, -0.6044, 21.7402, 32.4649, -1.1213, 23.412498, 29.679499, -0.6044, 23.412498, 29.679499, -0.6044, 23.412498, 32.4649, -1.1213, 23.412498, 32.4649, -1.1213, 25.084799, 29.679499, -0.6044, 23.412498, 32.4649, -1.1213, 25.084799, 29.679499, -0.6044, 25.084799, 29.679499, -0.6044, 25.084799, 32.4649, -1.1213, 25.084799, 32.4649, -1.1213, 26.7572, 29.679499, -0.6044, 25.084799, 32.4649, -1.1213, 26.7572, 29.679499, -0.6044, 26.7572, 32.4649, -1.1213, -15.0508995, 39.7973, -3.9413, -13.3786, 32.4649, -1.1213, -13.3786, 32.4649, -1.1213, -13.3786, 39.7973, -3.9413, -13.3786, 39.7973, -3.9413, -11.7063, 32.4649, -1.1213, -15.0508995, 39.7973, -3.9413, -15.0508995, 39.7973, -3.9413, -13.3786, 32.4649, -1.1213, -13.3786, 39.7973, -3.9413, -11.7063, 32.4649, -1.1213, -11.7063, 32.4649, -1.1213, -16.7232, 39.7973, -3.9413, -15.0508995, 32.4649, -1.1213, -15.0508995, 32.4649, -1.1213, -11.7063, 39.7973, -3.9413, -11.7063, 39.7973, -3.9413, -10.033899, 32.4649, -1.1213, -16.7232, 39.7973, -3.9413, -16.7232, 39.7973, -3.9413, -15.0508995, 32.4649, -1.1213, -11.7063, 39.7973, -3.9413, -10.033899, 32.4649, -1.1213, -10.033899, 32.4649, -1.1213, -18.3955, 39.7973, -3.9413, -16.7232, 32.4649, -1.1213, -16.7232, 32.4649, -1.1213, -10.033899, 39.7973, -3.9413, -10.033899, 39.7973, -3.9413, -8.3616, 32.4649, -1.1213, -18.3955, 39.7973, -3.9413, -18.3955, 39.7973, -3.9413, -16.7232, 32.4649, -1.1213, -10.033899, 39.7973, -3.9413, -8.3616, 32.4649, -1.1213, -8.3616, 32.4649, -1.1213, -20.0679, 39.7973, -3.9413, -18.3955, 32.4649, -1.1213, -18.3955, 32.4649, -1.1213, -8.3616, 39.7973, -3.9413, -8.3616, 39.7973, -3.9413, -6.6893, 32.4649, -1.1213, -20.0679, 39.7973, -3.9413, -20.0679, 39.7973, -3.9413, -18.3955, 32.4649, -1.1213, -8.3616, 39.7973, -3.9413, -6.6893, 32.4649, -1.1213, -6.6893, 32.4649, -1.1213, -21.7402, 39.7973, -3.9413, -20.0679, 32.4649, -1.1213, -20.0679, 32.4649, -1.1213, -6.6893, 39.7973, -3.9413, -6.6893, 39.7973, -3.9413, -5.0169997, 32.4649, -1.1213, -21.7402, 39.7973, -3.9413, -21.7402, 39.7973, -3.9413, -20.0679, 32.4649, -1.1213, -6.6893, 39.7973, -3.9413, -5.0169997, 32.4649, -1.1213, -5.0169997, 32.4649, -1.1213, -23.412498, 39.7973, -3.9413, -21.7402, 32.4649, -1.1213, -21.7402, 32.4649, -1.1213, -5.0169997, 39.7973, -3.9413, -5.0169997, 39.7973, -3.9413, -3.3446, 32.4649, -1.1213, -23.412498, 39.7973, -3.9413, -23.412498, 39.7973, -3.9413, -21.7402, 32.4649, -1.1213, -5.0169997, 39.7973, -3.9413, -3.3446, 32.4649, -1.1213, -3.3446, 32.4649, -1.1213, -25.084799, 39.7973, -3.9413, -23.412498, 32.4649, -1.1213, -23.412498, 32.4649, -1.1213, -3.3446, 39.7973, -3.9413, -3.3446, 39.7973, -3.9413, -1.6723, 32.4649, -1.1213, -25.084799, 39.7973, -3.9413, -25.084799, 39.7973, -3.9413, -23.412498, 32.4649, -1.1213, -26.7572, 39.7973, -3.9413, -25.084799, 32.4649, -1.1213, -25.084799, 32.4649, -1.1213, -26.7572, 39.7973, -3.9413, -26.7572, 39.7973, -3.9413, -25.084799, 32.4649, -1.1213, -3.3446, 39.7973, -3.9413, -1.6723, 32.4649, -1.1213, -1.6723, 32.4649, -1.1213, -1.6723, 39.7973, -3.9413, -1.6723, 39.7973, -3.9413, 0, 32.4649, -1.1213, -1.6723, 39.7973, -3.9413, 0, 32.4649, -1.1213, 0, 32.4649, -1.1213, 0, 39.7973, -3.9413, 0, 39.7973, -3.9413, 1.6723, 32.4649, -1.1213, 0, 39.7973, -3.9413, 1.6723, 32.4649, -1.1213, 1.6723, 32.4649, -1.1213, 1.6723, 39.7973, -3.9413, 1.6723, 39.7973, -3.9413, 3.3446, 32.4649, -1.1213, 1.6723, 39.7973, -3.9413, 3.3446, 32.4649, -1.1213, 3.3446, 32.4649, -1.1213, 3.3446, 39.7973, -3.9413, 3.3446, 39.7973, -3.9413, 5.0169997, 32.4649, -1.1213, 3.3446, 39.7973, -3.9413, 5.0169997, 32.4649, -1.1213, 5.0169997, 32.4649, -1.1213, 5.0169997, 39.7973, -3.9413, 5.0169997, 39.7973, -3.9413, 6.6893, 32.4649, -1.1213, 5.0169997, 39.7973, -3.9413, 6.6893, 32.4649, -1.1213, 6.6893, 32.4649, -1.1213, 6.6893, 39.7973, -3.9413, 6.6893, 39.7973, -3.9413, 8.3616, 32.4649, -1.1213, 6.6893, 39.7973, -3.9413, 8.3616, 32.4649, -1.1213, 8.3616, 32.4649, -1.1213, 8.3616, 39.7973, -3.9413, 8.3616, 39.7973, -3.9413, 10.033899, 32.4649, -1.1213, 8.3616, 39.7973, -3.9413, 10.033899, 32.4649, -1.1213, 10.033899, 32.4649, -1.1213, 10.033899, 39.7973, -3.9413, 10.033899, 39.7973, -3.9413, 11.7063, 32.4649, -1.1213, 10.033899, 39.7973, -3.9413, 11.7063, 32.4649, -1.1213, 11.7063, 32.4649, -1.1213, 11.7063, 39.7973, -3.9413, 11.7063, 39.7973, -3.9413, 13.3786, 32.4649, -1.1213, 11.7063, 39.7973, -3.9413, 13.3786, 32.4649, -1.1213, 13.3786, 32.4649, -1.1213, 13.3786, 39.7973, -3.9413, 13.3786, 39.7973, -3.9413, 15.0508995, 32.4649, -1.1213, 13.3786, 39.7973, -3.9413, 15.0508995, 32.4649, -1.1213, 15.0508995, 32.4649, -1.1213, 15.0508995, 39.7973, -3.9413, 15.0508995, 39.7973, -3.9413, 16.7232, 32.4649, -1.1213, 15.0508995, 39.7973, -3.9413, 16.7232, 32.4649, -1.1213, 16.7232, 32.4649, -1.1213, 16.7232, 39.7973, -3.9413, 16.7232, 39.7973, -3.9413, 18.3955, 32.4649, -1.1213, 16.7232, 39.7973, -3.9413, 18.3955, 32.4649, -1.1213, 18.3955, 32.4649, -1.1213, 18.3955, 39.7973, -3.9413, 18.3955, 39.7973, -3.9413, 20.0679, 32.4649, -1.1213, 18.3955, 39.7973, -3.9413, 20.0679, 32.4649, -1.1213, 20.0679, 32.4649, -1.1213, 20.0679, 39.7973, -3.9413, 20.0679, 39.7973, -3.9413, 21.7402, 32.4649, -1.1213, 20.0679, 39.7973, -3.9413, 21.7402, 32.4649, -1.1213, 21.7402, 32.4649, -1.1213, 21.7402, 39.7973, -3.9413, 21.7402, 39.7973, -3.9413, 23.412498, 32.4649, -1.1213, 21.7402, 39.7973, -3.9413, 23.412498, 32.4649, -1.1213, 23.412498, 32.4649, -1.1213, 23.412498, 39.7973, -3.9413, 23.412498, 39.7973, -3.9413, 25.084799, 32.4649, -1.1213, 23.412498, 39.7973, -3.9413, 25.084799, 32.4649, -1.1213, 25.084799, 32.4649, -1.1213, 25.084799, 39.7973, -3.9413, 25.084799, 39.7973, -3.9413, 26.7572, 32.4649, -1.1213, 25.084799, 39.7973, -3.9413, 26.7572, 32.4649, -1.1213, 26.7572, 26.7572, -0.2593, 16.7232, 29.679499, -0.6044, 18.3955, 26.7572, -0.2593, 18.3955, 26.7572, -0.2593, 18.3955, 29.679499, -0.6044, 18.3955, 29.679499, -0.6044, 20.0679, 26.7572, -0.2593, 16.7232, 29.679499, -0.6044, 16.7232, 29.679499, -0.6044, 18.3955, 26.7572, -0.2593, 18.3955, 29.679499, -0.6044, 20.0679, 26.7572, -0.2593, 20.0679, 26.7572, -0.2593, 15.0508995, 29.679499, -0.6044, 16.7232, 26.7572, -0.2593, 16.7232, 26.7572, -0.2593, 20.0679, 29.679499, -0.6044, 20.0679, 29.679499, -0.6044, 21.7402, 26.7572, -0.2593, 15.0508995, 29.679499, -0.6044, 15.0508995, 29.679499, -0.6044, 16.7232, 26.7572, -0.2593, 20.0679, 29.679499, -0.6044, 21.7402, 26.7572, -0.2593, 21.7402, 26.7572, -0.2593, 13.3786, 29.679499, -0.6044, 15.0508995, 26.7572, -0.2593, 15.0508995, 26.7572, -0.2593, 21.7402, 29.679499, -0.6044, 21.7402, 29.679499, -0.6044, 23.412498, 26.7572, -0.2593, 13.3786, 29.679499, -0.6044, 13.3786, 29.679499, -0.6044, 15.0508995, 26.7572, -0.2593, 21.7402, 29.679499, -0.6044, 23.412498, 26.7572, -0.2593, 23.412498, 26.7572, -0.2593, 11.7063, 29.679499, -0.6044, 13.3786, 26.7572, -0.2593, 13.3786, 26.7572, -0.2593, 23.412498, 29.679499, -0.6044, 23.412498, 29.679499, -0.6044, 25.084799, 26.7572, -0.2593, 11.7063, 29.679499, -0.6044, 11.7063, 29.679499, -0.6044, 13.3786, 26.7572, -0.2593, 23.412498, 29.679499, -0.6044, 25.084799, 26.7572, -0.2593, 25.084799, 26.7572, -0.2593, 25.084799, 29.679499, -0.6044, 25.084799, 29.679499, -0.6044, 26.7572, 26.7572, -0.2593, 25.084799, 29.679499, -0.6044, 26.7572, 26.7572, -0.2593, 26.7572, 26.7572, -0.2593, 10.033899, 29.679499, -0.6044, 11.7063, 26.7572, -0.2593, 11.7063, 26.7572, -0.2593, 10.033899, 29.679499, -0.6044, 10.033899, 29.679499, -0.6044, 11.7063, 26.7572, -0.2593, 8.3616, 29.679499, -0.6044, 10.033899, 26.7572, -0.2593, 10.033899, 26.7572, -0.2593, 8.3616, 29.679499, -0.6044, 8.3616, 29.679499, -0.6044, 10.033899, 26.7572, -0.2593, 6.6893, 29.679499, -0.6044, 8.3616, 26.7572, -0.2593, 8.3616, 26.7572, -0.2593, 6.6893, 29.679499, -0.6044, 6.6893, 29.679499, -0.6044, 8.3616, 26.7572, -0.2593, 5.0169997, 29.679499, -0.6044, 6.6893, 26.7572, -0.2593, 6.6893, 26.7572, -0.2593, 5.0169997, 29.679499, -0.6044, 5.0169997, 29.679499, -0.6044, 6.6893, 26.7572, -0.2593, 3.3446, 29.679499, -0.6044, 5.0169997, 26.7572, -0.2593, 5.0169997, 26.7572, -0.2593, 3.3446, 29.679499, -0.6044, 3.3446, 29.679499, -0.6044, 5.0169997, 26.7572, -0.2593, 1.6723, 29.679499, -0.6044, 3.3446, 26.7572, -0.2593, 3.3446, 26.7572, -0.2593, 1.6723, 29.679499, -0.6044, 1.6723, 29.679499, -0.6044, 3.3446, 26.7572, -0.2593, 0, 29.679499, -0.6044, 1.6723, 26.7572, -0.2593, 1.6723, 26.7572, -0.2593, 0, 29.679499, -0.6044, 0, 29.679499, -0.6044, 1.6723, 26.7572, -0.2593, -1.6723, 29.679499, -0.6044, 0, 26.7572, -0.2593, 0, 26.7572, -0.2593, -1.6723, 29.679499, -0.6044, -1.6723, 29.679499, -0.6044, 0, 26.7572, -0.2593, -3.3446, 29.679499, -0.6044, -1.6723, 26.7572, -0.2593, -1.6723, 26.7572, -0.2593, -3.3446, 29.679499, -0.6044, -3.3446, 29.679499, -0.6044, -1.6723, 26.7572, -0.2593, -5.0169997, 29.679499, -0.6044, -3.3446, 26.7572, -0.2593, -3.3446, 26.7572, -0.2593, -5.0169997, 29.679499, -0.6044, -5.0169997, 29.679499, -0.6044, -3.3446, 26.7572, -0.2593, -6.6893, 29.679499, -0.6044, -5.0169997, 26.7572, -0.2593, -5.0169997, 26.7572, -0.2593, -6.6893, 29.679499, -0.6044, -6.6893, 29.679499, -0.6044, -5.0169997, 26.7572, -0.2593, -8.3616, 29.679499, -0.6044, -6.6893, 26.7572, -0.2593, -6.6893, 26.7572, -0.2593, -8.3616, 29.679499, -0.6044, -8.3616, 29.679499, -0.6044, -6.6893, 26.7572, -0.2593, -10.033899, 29.679499, -0.6044, -8.3616, 26.7572, -0.2593, -8.3616, 26.7572, -0.2593, -10.033899, 29.679499, -0.6044, -10.033899, 29.679499, -0.6044, -8.3616, 26.7572, -0.2593, -11.7063, 29.679499, -0.6044, -10.033899, 26.7572, -0.2593, -10.033899, 26.7572, -0.2593, -11.7063, 29.679499, -0.6044, -11.7063, 29.679499, -0.6044, -10.033899, 26.7572, -0.2593, -13.3786, 29.679499, -0.6044, -11.7063, 26.7572, -0.2593, -11.7063, 26.7572, -0.2593, -13.3786, 29.679499, -0.6044, -13.3786, 29.679499, -0.6044, -11.7063, 26.7572, -0.2593, -15.0508995, 29.679499, -0.6044, -13.3786, 26.7572, -0.2593, -13.3786, 26.7572, -0.2593, -15.0508995, 29.679499, -0.6044, -15.0508995, 29.679499, -0.6044, -13.3786, 26.7572, -0.2593, -16.7232, 29.679499, -0.6044, -15.0508995, 26.7572, -0.2593, -15.0508995, 26.7572, -0.2593, -16.7232, 29.679499, -0.6044, -16.7232, 29.679499, -0.6044, -15.0508995, 26.7572, -0.2593, -18.3955, 29.679499, -0.6044, -16.7232, 26.7572, -0.2593, -16.7232, 26.7572, -0.2593, -18.3955, 29.679499, -0.6044, -18.3955, 29.679499, -0.6044, -16.7232, 26.7572, -0.2593, -20.0679, 29.679499, -0.6044, -18.3955, 26.7572, -0.2593, -18.3955, 26.7572, -0.2593, -20.0679, 29.679499, -0.6044, -20.0679, 29.679499, -0.6044, -18.3955, 26.7572, -0.2593, -21.7402, 29.679499, -0.6044, -20.0679, 26.7572, -0.2593, -20.0679, 26.7572, -0.2593, -21.7402, 29.679499, -0.6044, -21.7402, 29.679499, -0.6044, -20.0679, 26.7572, -0.2593, -23.412498, 29.679499, -0.6044, -21.7402, 26.7572, -0.2593, -21.7402, 26.7572, -0.2593, -23.412498, 29.679499, -0.6044, -23.412498, 29.679499, -0.6044, -21.7402, 26.7572, -0.2593, -25.084799, 29.679499, -0.6044, -23.412498, 26.7572, -0.2593, -23.412498, 26.7572, -0.2593, -25.084799, 29.679499, -0.6044, -25.084799, 29.679499, -0.6044, -23.412498, 26.7572, -0.2593, -26.7572, 29.679499, -0.6044, -25.084799, 26.7572, -0.2593, -25.084799, 26.7572, -0.2593, -26.7572, 29.679499, -0.6044, -26.7572, 29.679499, -0.6044, -25.084799) @@ -40,19 +37,23 @@ size = Vector2(10000, 10000) [sub_resource type="BoxShape3D" id="OceanWestShape"] resource_local_to_scene = true -size = Vector3(17, 2, 80) +size = Vector3(20.820662814, 2, 97.979589711) [sub_resource type="BoxShape3D" id="OceanEastShape"] resource_local_to_scene = true -size = Vector3(42, 2, 80) +size = Vector3(51.439284598, 2, 97.979589711) [sub_resource type="BoxShape3D" id="OceanNorthShape"] resource_local_to_scene = true -size = Vector3(48, 2, 18) +size = Vector3(58.787753827, 2, 22.045407685) [sub_resource type="BoxShape3D" id="OceanSouthShape"] resource_local_to_scene = true -size = Vector3(48, 2, 18) +size = Vector3(58.787753827, 2, 22.045407685) + +[sub_resource type="BoxShape3D" id="OceanCenterShape"] +resource_local_to_scene = true +size = Vector3(58.787753827, 2, 53.888774341) [sub_resource type="BoxShape3D" id="OceanWestRecoveryShape"] resource_local_to_scene = true @@ -70,29 +71,31 @@ size = Vector3(48, 5, 18) resource_local_to_scene = true size = Vector3(48, 5, 18) -[sub_resource type="Resource" id="ShorelineRibbonConfig_pond"] -script = ExtResource("15_shoreline_config") -terrain_source = NodePath("../Terrain/Collision/Shape") +[sub_resource type="Resource" id="ShorelineRibbonConfigPond"] +script = ExtResource("22_shoreline_config") +terrain_source = NodePath("../Terrain/Visual/starter_island") water_height = 2.51 generation_bounds = Rect2(-17.45, -4.08375, 16.1, 12.3675) water_reference = Vector2(-9.4, 2.1) water_is_inside = true output_resource_path = "res://world/generated/shorelines/starter_pond_shoreline.tres" -[sub_resource type="Resource" id="ShorelineRibbonConfig_ocean"] -script = ExtResource("15_shoreline_config") -terrain_source = NodePath("../Terrain/Collision/Shape") +[sub_resource type="Resource" id="ShorelineRibbonConfigOcean"] +script = ExtResource("22_shoreline_config") +terrain_source = NodePath("../Terrain/Visual/starter_island") water_type = 1 water_height = -0.45 generation_bounds = Rect2(-42, -50, 108, 100) water_reference = Vector2(0, 0) water_is_inside = false output_resource_path = "res://world/generated/shorelines/starter_ocean_shoreline.tres" +simplification_tolerance = 0.55 +smoothing_iterations = 3 +resample_spacing = 0.16 +corner_rounding_distance = 2.4 [node name="StarterIslandRegion" type="Node3D" unique_id=1830415700] script = ExtResource("1_region") -grass_material = ExtResource("9_grass") -sand_material = ExtResource("11_sand") region_id = &"starter_island" fishable_water_root = NodePath("WaterBodies") water_recovery_root = NodePath("WaterBodies") @@ -107,14 +110,14 @@ water_recovery_root = NodePath("WaterBodies") shape = SubResource("ConcavePolygonShape3D_4e4k5") [node name="ShorelineRibbonBaker" type="Node" parent="."] -script = ExtResource("14_shoreline_baker") -water_bodies = Array[ExtResource("15_shoreline_config")]([SubResource("ShorelineRibbonConfig_pond"), SubResource("ShorelineRibbonConfig_ocean")]) +script = ExtResource("21_shoreline_baker") +water_bodies = Array[ExtResource("22_shoreline_config")]([SubResource("ShorelineRibbonConfigPond"), SubResource("ShorelineRibbonConfigOcean")]) [node name="ShorelineRibbons" type="Node3D" parent="."] [node name="Ocean" type="MeshInstance3D" parent="ShorelineRibbons"] +visible = false cast_shadow = 0 -material_override = ExtResource("18_shoreline_material") mesh = ExtResource("17_ocean_shoreline") [node name="WaterBodies" type="Node3D" parent="." unique_id=1417012172] @@ -162,6 +165,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, -0.45, 0) [node name="VisualWater" type="MeshInstance3D" parent="WaterBodies/Ocean" unique_id=1176408937] material_override = ExtResource("13_water") mesh = SubResource("OceanWaterMesh") +script = ExtResource("20_surface_motion") [node name="FishingRegions" type="Node3D" parent="WaterBodies/Ocean" unique_id=205598473] @@ -176,21 +180,25 @@ fish_pool = ExtResource("4_pool") surface_height_mode = 1 [node name="WestShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=357406062] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45.5, -1, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -55.613519213, -1, 0) shape = SubResource("OceanWestShape") [node name="EastShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=772224738] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 32, -1, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 39.30420832, -1, 0) shape = SubResource("OceanEastShape") [node name="NorthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=646147646] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, 31) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15.809310892, -1, 37.967091013) shape = SubResource("OceanNorthShape") [node name="SouthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=894083854] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, -31) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15.809310892, -1, -37.967091013) shape = SubResource("OceanSouthShape") +[node name="CenterShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion"] +position = Vector3(-15.809310892, -1, 0) +shape = SubResource("OceanCenterShape") + [node name="RecoveryRegions" type="Node3D" parent="WaterBodies/Ocean" unique_id=463706703] [node name="OceanRecoveryRegion" type="Area3D" parent="WaterBodies/Ocean/RecoveryRegions" unique_id=922834963] @@ -248,7 +256,4 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.95, 13) [node name="Interactables" type="Node3D" parent="." unique_id=26695257] [node name="FishingShopWorld" parent="Interactables" unique_id=1255373524 instance=ExtResource("7_shop")] -transform = Transform3D(0.90630776, 0, -0.42261827, 0, 1, 0, 0.42261827, 0, 0.90630776, 8, 3.42, 6) - -[node name="PelicanCoolerPerch" parent="Interactables" unique_id=588122038 instance=ExtResource("8_pelican")] -transform = Transform3D(0.81915206, 0, -0.57357645, 0, 1, 0, 0.57357645, 0, 0.81915206, 18, 0.35, -16) +transform = Transform3D(-0.9976046, 0, -0.06917416, 0, 1, 0, 0.06917416, 0, -0.9976046, 5.976795, 3.42, 4.299163) diff --git a/world/shoreline_ambience.gd b/world/shoreline_ambience.gd new file mode 100644 index 0000000..81e9c65 --- /dev/null +++ b/world/shoreline_ambience.gd @@ -0,0 +1,157 @@ +class_name ShorelineAmbience +extends Node + +@export_range(0.0, 20.0, 0.1) var near_distance: float = 2.0 +@export_range(1.0, 100.0, 0.5) var far_distance: float = 24.0 +@export_range(-40.0, 12.0, 0.5) var near_volume_db: float = 1.0 +@export_range(-80.0, 0.0, 0.5) var far_volume_db: float = -18.0 +@export_range(0.05, 1.0, 0.05) var distance_update_interval: float = 0.2 +@export_range(0.1, 20.0, 0.1) var volume_smoothing_speed: float = 4.0 + +@onready var _waves_audio: AudioStreamPlayer = %WavesAudio + +var _listener: Node3D +var _shoreline_segments: Array[PackedVector2Array] = [] +var _distance_update_remaining: float = 0.0 +var _target_volume_db: float = -80.0 +var _is_active := false + + +func _ready() -> void: + _waves_audio.bus = &"Environment" + _configure_audio_loop(_waves_audio.stream) + _waves_audio.volume_db = -80.0 + set_process(false) + + +func configure(listener: Node3D, shoreline_mesh: MeshInstance3D) -> void: + _listener = listener + _shoreline_segments = _extract_shoreline_segments(shoreline_mesh) + _distance_update_remaining = 0.0 + if _shoreline_segments.is_empty(): + push_warning("Saltwater shoreline ambience has no coastline geometry.") + + +func set_active(active: bool) -> void: + _is_active = active + set_process(active) + if active: + _distance_update_remaining = 0.0 + _update_target_volume() + _waves_audio.volume_db = _target_volume_db + if not _waves_audio.playing and _waves_audio.stream != null: + _waves_audio.play() + else: + _waves_audio.stop() + _waves_audio.volume_db = -80.0 + + +func _process(delta: float) -> void: + if not _is_active: + return + _distance_update_remaining -= delta + if _distance_update_remaining <= 0.0: + _distance_update_remaining = distance_update_interval + _update_target_volume() + _waves_audio.volume_db = lerpf( + _waves_audio.volume_db, + _target_volume_db, + 1.0 - exp(-volume_smoothing_speed * delta), + ) + + +func _update_target_volume() -> void: + if _listener == null or _shoreline_segments.is_empty(): + _target_volume_db = -80.0 + return + var listener_position := Vector2( + _listener.global_position.x, + _listener.global_position.z, + ) + var distance := distance_to_segments(listener_position, _shoreline_segments) + var blend := clampf( + inverse_lerp(near_distance, maxf(near_distance + 0.01, far_distance), distance), + 0.0, + 1.0, + ) + _target_volume_db = lerpf(near_volume_db, far_volume_db, smoothstep(0.0, 1.0, blend)) + + +func _extract_shoreline_segments( + shoreline_mesh: MeshInstance3D, +) -> Array[PackedVector2Array]: + var segments: Array[PackedVector2Array] = [] + var seen_segments: Dictionary = {} + if shoreline_mesh == null or shoreline_mesh.mesh == null: + return segments + for surface_index in shoreline_mesh.mesh.get_surface_count(): + var arrays := shoreline_mesh.mesh.surface_get_arrays(surface_index) + var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX] + var indices: PackedInt32Array = arrays[Mesh.ARRAY_INDEX] + if indices.is_empty(): + continue + for index_offset in range(0, indices.size() - 2, 3): + var triangle := PackedVector2Array() + for corner in 3: + var vertex := shoreline_mesh.to_global( + vertices[indices[index_offset + corner]] + ) + triangle.append(Vector2(vertex.x, vertex.z)) + _append_unique_segment(segments, seen_segments, triangle[0], triangle[1]) + _append_unique_segment(segments, seen_segments, triangle[1], triangle[2]) + _append_unique_segment(segments, seen_segments, triangle[2], triangle[0]) + return segments + + +func _append_unique_segment( + segments: Array[PackedVector2Array], + seen_segments: Dictionary, + start: Vector2, + end: Vector2, +) -> void: + if start.distance_squared_to(end) <= 0.000001: + return + # The baked ribbon repeats shared triangle edges. Quantized canonical keys + # keep only distinct segments without an O(n²) startup pass. + var first := start + var second := end + if first.x > second.x or (is_equal_approx(first.x, second.x) and first.y > second.y): + first = end + second = start + var key := "%d,%d:%d,%d" % [ + roundi(first.x * 10000.0), + roundi(first.y * 10000.0), + roundi(second.x * 10000.0), + roundi(second.y * 10000.0), + ] + if seen_segments.has(key): + return + seen_segments[key] = true + segments.append(PackedVector2Array([start, end])) + + +static func distance_to_segments( + point: Vector2, + segments: Array[PackedVector2Array], +) -> float: + var closest_squared := INF + for segment in segments: + if segment.size() < 2: + continue + var closest := Geometry2D.get_closest_point_to_segment( + point, + segment[0], + segment[1], + ) + closest_squared = minf(closest_squared, point.distance_squared_to(closest)) + return sqrt(closest_squared) if closest_squared < INF else INF + + +func _configure_audio_loop(stream: AudioStream) -> void: + var wav := stream as AudioStreamWAV + if wav == null: + return + wav.loop_mode = AudioStreamWAV.LOOP_FORWARD + if wav.loop_end <= wav.loop_begin: + wav.loop_begin = 0 + wav.loop_end = int(round(wav.get_length() * wav.mix_rate)) diff --git a/world/shoreline_ambience.gd.uid b/world/shoreline_ambience.gd.uid new file mode 100644 index 0000000..3188ec7 --- /dev/null +++ b/world/shoreline_ambience.gd.uid @@ -0,0 +1 @@ +uid://b0c3o2vy76fg3 diff --git a/world/test_world.gd b/world/test_world.gd index 189c119..a3ea397 100644 --- a/world/test_world.gd +++ b/world/test_world.gd @@ -57,8 +57,8 @@ func get_fishable_water_regions() -> Array[FishableWaterRegion]: return waters -func get_pelican_convenience_landmark() -> Node3D: - return _starter_island.get_pelican_landmark() +func get_saltwater_shoreline_mesh() -> MeshInstance3D: + return _starter_island.get_saltwater_shoreline_mesh() func _get_regions() -> Array[WorldRegion]: diff --git a/world/test_world.tscn b/world/test_world.tscn index ef19355..b6345fb 100644 --- a/world/test_world.tscn +++ b/world/test_world.tscn @@ -6,13 +6,13 @@ [ext_resource type="Environment" path="res://world/environment/netfishing_environment.tres" id="4_environment"] [sub_resource type="BoxShape3D" id="NorthSouthBoundsShape"] -size = Vector3(120, 14, 2) +size = Vector3(168.8772, 14, 2) [sub_resource type="BoxShape3D" id="EastWestBoundsShape"] -size = Vector3(2, 14, 90) +size = Vector3(2, 14, 126.45079) [sub_resource type="BoxShape3D" id="FailsafeShape"] -size = Vector3(118, 4, 88) +size = Vector3(166.8772, 4, 124.45079) [node name="TestWorld" type="Node3D"] script = ExtResource("1_world") @@ -49,25 +49,25 @@ shape = SubResource("FailsafeShape") [node name="WorldBounds" type="Node3D" parent="."] [node name="North" type="StaticBody3D" parent="WorldBounds"] -position = Vector3(12, 5, 45) +position = Vector3(12, 5, 63.225395) [node name="Shape" type="CollisionShape3D" parent="WorldBounds/North"] shape = SubResource("NorthSouthBoundsShape") [node name="South" type="StaticBody3D" parent="WorldBounds"] -position = Vector3(12, 5, -45) +position = Vector3(12, 5, -63.225395) [node name="Shape" type="CollisionShape3D" parent="WorldBounds/South"] shape = SubResource("NorthSouthBoundsShape") [node name="West" type="StaticBody3D" parent="WorldBounds"] -position = Vector3(-48, 5, 0) +position = Vector3(-72.4386, 5, 0) [node name="Shape" type="CollisionShape3D" parent="WorldBounds/West"] shape = SubResource("EastWestBoundsShape") [node name="East" type="StaticBody3D" parent="WorldBounds"] -position = Vector3(72, 5, 0) +position = Vector3(96.4386, 5, 0) [node name="Shape" type="CollisionShape3D" parent="WorldBounds/East"] shape = SubResource("EastWestBoundsShape") diff --git a/world/water/shoreline_ribbon_baker.gd b/world/water/shoreline_ribbon_baker.gd index 32c75d6..a88b6e4 100644 --- a/world/water/shoreline_ribbon_baker.gd +++ b/world/water/shoreline_ribbon_baker.gd @@ -51,7 +51,8 @@ func _rebuild(configuration: ShorelineRibbonConfig) -> Dictionary: configuration.water_is_inside, configuration.simplification_tolerance, configuration.smoothing_iterations, - configuration.resample_spacing + configuration.resample_spacing, + configuration.corner_rounding_distance, ) var output_directory := configuration.output_resource_path.get_base_dir() DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(output_directory)) diff --git a/world/water/shoreline_ribbon_config.gd b/world/water/shoreline_ribbon_config.gd index ae58496..682bfed 100644 --- a/world/water/shoreline_ribbon_config.gd +++ b/world/water/shoreline_ribbon_config.gd @@ -12,3 +12,4 @@ extends Resource @export var simplification_tolerance := -1.0 @export_range(-1, 4, 1) var smoothing_iterations := -1 @export var resample_spacing := -1.0 +@export var corner_rounding_distance := -1.0 diff --git a/world/water/shoreline_ribbon_generator.gd b/world/water/shoreline_ribbon_generator.gd index 5531ab8..9708b06 100644 --- a/world/water/shoreline_ribbon_generator.gd +++ b/world/water/shoreline_ribbon_generator.gd @@ -5,11 +5,16 @@ const WATER_PLANE_EPSILON := 0.001 const ENDPOINT_MERGE_TOLERANCE := 0.03 const LOOP_CLOSURE_TOLERANCE := 0.06 const MINIMUM_FRAGMENT_LENGTH := 2.0 -const SIMPLIFICATION_TOLERANCE := 0.20 -const SMOOTHING_ITERATIONS := 2 -const RESAMPLE_SPACING := 0.22 -const RIBBON_WIDTH := 0.85 -const LAND_INSET := 0.10 +const SIMPLIFICATION_TOLERANCE := 0.55 +const SMOOTHING_ITERATIONS := 3 +const RESAMPLE_SPACING := 0.16 +const CORNER_ROUNDING_DISTANCE := 2.4 +const CORNER_ROUNDING_START_DEGREES := 24.0 +const CORNER_ROUNDING_FULL_DEGREES := 78.0 +const MAXIMUM_JOIN_SCALE := 1.0 +const RIBBON_REACH_GROWTH_PER_METER := 0.52 +const RIBBON_WIDTH := 1.35 +const LAND_INSET := 0.12 const SURFACE_OFFSET := 0.018 @@ -21,7 +26,8 @@ static func generate( water_is_inside: bool, simplification_override := -1.0, smoothing_iterations_override := -1, - resample_spacing_override := -1.0 + resample_spacing_override := -1.0, + corner_rounding_override := -1.0 ) -> Dictionary: var simplification := ( SIMPLIFICATION_TOLERANCE @@ -38,6 +44,11 @@ static func generate( if resample_spacing_override < 0.0 else resample_spacing_override ) + var corner_rounding := ( + CORNER_ROUNDING_DISTANCE + if corner_rounding_override < 0.0 + else corner_rounding_override + ) var segments := _extract_segments(faces, water_height, bounds) var raw_paths := _stitch_segments(segments) var processed_paths: Array[Dictionary] = [] @@ -52,7 +63,12 @@ static func generate( continue var simplified := _simplify(raw_points, closed, simplification) simplified_paths.append({"points": simplified, "closed": closed}) - var smoothed := _chaikin(simplified, closed, smoothing_iterations) + var rounded := _round_sharp_corners( + simplified, + closed, + corner_rounding, + ) + var smoothed := _chaikin(rounded, closed, smoothing_iterations) var resampled := _resample(smoothed, closed, resample_spacing) if resampled.size() < (3 if closed else 2): continue @@ -296,6 +312,65 @@ static func _chaikin( return result +static func _round_sharp_corners( + points: PackedVector2Array, + closed: bool, + rounding_distance: float, +) -> PackedVector2Array: + if rounding_distance <= WATER_PLANE_EPSILON or points.size() < 3: + return points + var result := PackedVector2Array() + for index: int in points.size(): + if not closed and (index == 0 or index == points.size() - 1): + result.append(points[index]) + continue + var previous := points[(index - 1 + points.size()) % points.size()] + var point := points[index] + var following := points[(index + 1) % points.size()] + var incoming_vector := point - previous + var outgoing_vector := following - point + var incoming_length := incoming_vector.length() + var outgoing_length := outgoing_vector.length() + if ( + incoming_length <= WATER_PLANE_EPSILON + or outgoing_length <= WATER_PLANE_EPSILON + ): + result.append(point) + continue + var incoming := incoming_vector / incoming_length + var outgoing := outgoing_vector / outgoing_length + var turn_degrees := rad_to_deg( + acos(clampf(incoming.dot(outgoing), -1.0, 1.0)) + ) + var corner_weight := smoothstep( + CORNER_ROUNDING_START_DEGREES, + CORNER_ROUNDING_FULL_DEGREES, + turn_degrees, + ) + var cut_distance := minf( + rounding_distance * corner_weight, + minf(incoming_length, outgoing_length) * 0.44, + ) + if cut_distance <= WATER_PLANE_EPSILON: + result.append(point) + continue + var entry := point - incoming * cut_distance + var exit := point + outgoing * cut_distance + var curve_steps := maxi(3, ceili(cut_distance / 0.22)) + for step: int in curve_steps + 1: + var amount := float(step) / float(curve_steps) + var first := entry.lerp(point, amount) + var second := point.lerp(exit, amount) + var rounded_point := first.lerp(second, amount) + if ( + result.is_empty() + or result[result.size() - 1].distance_to(rounded_point) + > WATER_PLANE_EPSILON + ): + result.append(rounded_point) + return result + + static func _resample( points: PackedVector2Array, closed: bool, @@ -342,26 +417,77 @@ static func _build_mesh( for path_data: Dictionary in paths: var points: PackedVector2Array = path_data["points"] var closed: bool = path_data["closed"] + var closed_water_side := ( + _closed_path_water_side(points, water_is_inside) + if closed + else 0.0 + ) var base_index := vertices.size() - var path_distance := 0.0 + var water_normals := PackedVector2Array() + var join_scales := PackedFloat32Array() + var water_reaches := PackedFloat32Array() for index: int in points.size(): - if index > 0: - path_distance += points[index - 1].distance_to(points[index]) var previous := points[(index - 1 + points.size()) % points.size()] var following := points[(index + 1) % points.size()] if not closed: previous = points[maxi(index - 1, 0)] following = points[mini(index + 1, points.size() - 1)] - var tangent := previous.direction_to(following) - var water_normal := Vector2(-tangent.y, tangent.x) - var toward_reference := points[index].direction_to(water_reference) - if ( - (water_is_inside and water_normal.dot(toward_reference) < 0.0) - or (not water_is_inside and water_normal.dot(toward_reference) > 0.0) - ): - water_normal = -water_normal - var land_point := points[index] - water_normal * LAND_INSET - var water_point := points[index] + water_normal * (RIBBON_WIDTH - LAND_INSET) + var point := points[index] + var incoming := previous.direction_to(point) + var outgoing := point.direction_to(following) + if incoming.is_zero_approx(): + incoming = outgoing + if outgoing.is_zero_approx(): + outgoing = incoming + var incoming_normal := Vector2(-incoming.y, incoming.x) + var outgoing_normal := Vector2(-outgoing.y, outgoing.x) + var water_normal := incoming_normal + outgoing_normal + if water_normal.is_zero_approx(): + water_normal = outgoing_normal + water_normal = water_normal.normalized() + if closed: + water_normal *= closed_water_side + else: + var toward_reference := point.direction_to(water_reference) + if ( + (water_is_inside and water_normal.dot(toward_reference) < 0.0) + or (not water_is_inside and water_normal.dot(toward_reference) > 0.0) + ): + water_normal = -water_normal + var join_scale := minf( + 1.0 / maxf(absf(water_normal.dot(outgoing_normal)), 0.55), + MAXIMUM_JOIN_SCALE, + ) + var water_reach := _safe_water_reach( + incoming, + outgoing, + water_normal, + previous.distance_to(point), + point.distance_to(following), + (RIBBON_WIDTH - LAND_INSET) * join_scale, + ) + water_normals.append(water_normal) + join_scales.append(join_scale) + water_reaches.append(water_reach) + water_reaches = _smooth_water_reaches( + points, + water_reaches, + closed, + ) + var path_distance := 0.0 + for index: int in points.size(): + if index > 0: + path_distance += points[index - 1].distance_to(points[index]) + var point := points[index] + var water_normal := water_normals[index] + var join_scale := join_scales[index] + var land_point := ( + point - water_normal * LAND_INSET * join_scale + ) + var water_point := ( + point + + water_normal * water_reaches[index] + ) vertices.append(Vector3(land_point.x, water_height + SURFACE_OFFSET, land_point.y)) vertices.append(Vector3(water_point.x, water_height + SURFACE_OFFSET, water_point.y)) normals.append(Vector3.UP) @@ -388,6 +514,71 @@ static func _build_mesh( return mesh +static func _safe_water_reach( + incoming: Vector2, + outgoing: Vector2, + water_normal: Vector2, + incoming_length: float, + outgoing_length: float, + desired_reach: float, +) -> float: + var turn_cross := incoming.cross(outgoing) + var water_side := incoming.cross(water_normal) + if turn_cross * water_side <= 0.0: + return desired_reach + var turn_angle := acos(clampf(incoming.dot(outgoing), -1.0, 1.0)) + if turn_angle <= WATER_PLANE_EPSILON: + return desired_reach + var radius := minf(incoming_length, outgoing_length) / maxf( + 2.0 * sin(turn_angle * 0.5), + WATER_PLANE_EPSILON, + ) + return minf(desired_reach, maxf(radius * 0.58, 0.24)) + + +static func _smooth_water_reaches( + points: PackedVector2Array, + reaches: PackedFloat32Array, + closed: bool, +) -> PackedFloat32Array: + var result := reaches.duplicate() + if result.size() < 2: + return result + for _pass: int in 3: + var forward_start := 0 if closed else 1 + for index: int in range(forward_start, result.size()): + var previous := (index - 1 + result.size()) % result.size() + var allowed := ( + result[previous] + + points[previous].distance_to(points[index]) + * RIBBON_REACH_GROWTH_PER_METER + ) + result[index] = minf(result[index], allowed) + var backward_start := result.size() - 1 if closed else result.size() - 2 + for index: int in range(backward_start, -1, -1): + var following := (index + 1) % result.size() + var allowed := ( + result[following] + + points[index].distance_to(points[following]) + * RIBBON_REACH_GROWTH_PER_METER + ) + result[index] = minf(result[index], allowed) + return result + + +static func _closed_path_water_side( + points: PackedVector2Array, + water_is_inside: bool, +) -> float: + var signed_area_twice := 0.0 + for index: int in points.size(): + signed_area_twice += points[index].cross( + points[(index + 1) % points.size()] + ) + var interior_side := 1.0 if signed_area_twice >= 0.0 else -1.0 + return interior_side if water_is_inside else -interior_side + + static func _point_key(point: Vector2) -> Vector2i: return Vector2i( roundi(point.x / ENDPOINT_MERGE_TOLERANCE), @@ -440,4 +631,6 @@ static func _mesh_triangle_count(mesh: ArrayMesh) -> int: if mesh.get_surface_count() == 0: return 0 var arrays := mesh.surface_get_arrays(0) - return (arrays[Mesh.ARRAY_INDEX] as PackedInt32Array).size() / 3 + return floori( + float((arrays[Mesh.ARRAY_INDEX] as PackedInt32Array).size()) / 3.0 + ) diff --git a/world/water_surface_motion.gd b/world/water_surface_motion.gd new file mode 100644 index 0000000..992f9cb --- /dev/null +++ b/world/water_surface_motion.gd @@ -0,0 +1,68 @@ +class_name WaterSurfaceMotion +extends MeshInstance3D + +const DEFAULT_AMPLITUDE: float = 0.055 +const DEFAULT_CYCLE_SECONDS: float = 7.0 +const DEFAULT_SECONDARY_SWELL: float = 0.18 +const DEFAULT_PHASE_OFFSET: float = 0.0 + +@export_range(0.0, 0.2, 0.005, "suffix:m") var amplitude: float = ( + DEFAULT_AMPLITUDE +) +@export_range(1.0, 30.0, 0.1, "suffix:s") var cycle_seconds: float = ( + DEFAULT_CYCLE_SECONDS +) +@export_range(0.0, 0.5, 0.01) var secondary_swell: float = ( + DEFAULT_SECONDARY_SWELL +) +@export_range(0.0, TAU, 0.01, "radians") var phase_offset: float = 0.0 + +var _base_height: float + + +func _ready() -> void: + _base_height = position.y + set_process(amplitude > 0.0) + + +func _process(_delta: float) -> void: + position.y = _base_height + calculate_height_offset( + _current_time_seconds(), + amplitude, + cycle_seconds, + secondary_swell, + phase_offset, + ) + + +static func get_default_height_offset() -> float: + return calculate_height_offset( + _current_time_seconds(), + DEFAULT_AMPLITUDE, + DEFAULT_CYCLE_SECONDS, + DEFAULT_SECONDARY_SWELL, + DEFAULT_PHASE_OFFSET, + ) + + +static func calculate_height_offset( + time_seconds: float, + motion_amplitude: float, + motion_cycle_seconds: float, + motion_secondary_swell: float, + motion_phase_offset: float, +) -> float: + var safe_cycle := maxf(motion_cycle_seconds, 0.001) + var phase := ( + fposmod(time_seconds, safe_cycle) / safe_cycle * TAU + + motion_phase_offset + ) + var swell := ( + sin(phase) + + sin(phase * 2.0 + 1.1) * motion_secondary_swell + ) / (1.0 + motion_secondary_swell) + return swell * motion_amplitude + + +static func _current_time_seconds() -> float: + return float(Time.get_ticks_msec()) * 0.001 diff --git a/world/water_surface_motion.gd.uid b/world/water_surface_motion.gd.uid new file mode 100644 index 0000000..65746a9 --- /dev/null +++ b/world/water_surface_motion.gd.uid @@ -0,0 +1 @@ +uid://drdjm443o35tc diff --git a/world/world_character_display.gd b/world/world_character_display.gd new file mode 100644 index 0000000..a145f3b --- /dev/null +++ b/world/world_character_display.gd @@ -0,0 +1,137 @@ +class_name WorldCharacterDisplay +extends Node3D + +const CharacterCustomizationCatalogType = preload( + "res://progression/character_customization_catalog.gd" +) +const PlayerVisualPresenterType = preload( + "res://player/player_visual_presenter.gd" +) + +@export_enum("round", "pointy") var species_id: String = "round" +@export_enum( + "white", + "gray", + "charcoal", + "brown", + "orange", + "yellow", + "green", + "teal", +) var fur_color_id: String = "white" +@export_enum( + "none", + "pointy_long", + "pointy_short", + "pointy_wide", +) var ears_id: String = "none" +@export_enum( + "none", + "bear", + "bunny", + "cat", + "fox", + "gator", + "pointy", +) var tail_id: String = "none" +@export var eyes_id: String = "simple_shine" +@export var nose_id: String = "dog_round" +@export var mouth_id: String = "three" +@export_category("Head Look") +@export var head_look_enabled: bool = false +@export_range(0.5, 10.0, 0.1, "suffix:m") var head_look_distance: float = 4.0 +@export_range(0.5, 3.0, 0.05, "suffix:m") var look_target_height: float = 1.25 +@export_range(0.0, 80.0, 1.0, "degrees") var head_yaw_limit: float = 50.0 +@export_range(0.0, 45.0, 1.0, "degrees") var head_pitch_limit: float = 22.0 +@export_range(0.05, 2.0, 0.05, "suffix:s") var head_blend_seconds: float = 0.3 + +var _head_look: LookAtModifier3D +var _skeleton: Skeleton3D +var _look_target: Node3D +var _look_target_proxy: Node3D +var _head_look_requested: bool = false + + +func _ready() -> void: + var appearance := CharacterCustomizationCatalogType.default_snapshot() + appearance["species"] = species_id + appearance["fur_pattern"] = fur_color_id + appearance["ears"] = ears_id + appearance["tail"] = tail_id + appearance["eyes"] = eyes_id + appearance["nose"] = nose_id + appearance["mouth"] = mouth_id + PlayerVisualPresenterType.apply_appearance(self, appearance) + _prepare_head_look() + + +func _process(delta: float) -> void: + if _head_look == null or _look_target_proxy == null: + return + var target_is_valid := ( + _look_target != null and is_instance_valid(_look_target) + ) + if target_is_valid: + _look_target_proxy.global_position = ( + _look_target.global_position + Vector3.UP * look_target_height + ) + var target_influence := ( + 1.0 if _head_look_requested and target_is_valid else 0.0 + ) + _head_look.influence = move_toward( + _head_look.influence, + target_influence, + delta / maxf(head_blend_seconds, 0.001), + ) + + +func set_head_look_target(target: Node3D) -> void: + _look_target = target + + +func set_head_look_active(active: bool) -> void: + _head_look_requested = active + + +func get_head_anchor_position() -> Vector3: + if _skeleton == null: + return global_position + Vector3.UP * 1.8 + var head_index := _skeleton.find_bone("head") + if head_index < 0: + return global_position + Vector3.UP * 1.8 + var head_pose := _skeleton.get_bone_global_pose(head_index) + return ( + (_skeleton.global_transform * head_pose).origin + + Vector3.UP * 0.85 + ) + + +func _prepare_head_look() -> void: + if not head_look_enabled: + set_process(false) + return + _skeleton = find_child("Skeleton3D", true, false) as Skeleton3D + if _skeleton == null or _skeleton.find_bone("head") < 0: + push_warning("World character head-look requires a head bone.") + set_process(false) + return + _look_target_proxy = Node3D.new() + _look_target_proxy.name = "HeadLookTarget" + add_child(_look_target_proxy) + _head_look = LookAtModifier3D.new() + _head_look.name = "HeadLookModifier" + _head_look.bone_name = "head" + _head_look.use_secondary_rotation = true + _head_look.use_angle_limitation = true + _head_look.symmetry_limitation = true + _head_look.primary_limit_angle = deg_to_rad(head_yaw_limit) + _head_look.secondary_limit_angle = deg_to_rad(head_pitch_limit) + _head_look.primary_damp_threshold = 0.65 + _head_look.secondary_damp_threshold = 0.65 + _head_look.duration = head_blend_seconds + _head_look.influence = 0.0 + _head_look.set("origin_from", 1) + _head_look.set("origin_bone_name", "head") + _skeleton.add_child(_head_look) + _head_look.target_node = _head_look.get_path_to(_look_target_proxy) + set_process(true) diff --git a/world/world_character_display.gd.uid b/world/world_character_display.gd.uid new file mode 100644 index 0000000..0873153 --- /dev/null +++ b/world/world_character_display.gd.uid @@ -0,0 +1 @@ +uid://dqsehnkidygg7 diff --git a/world/world_time_service.gd b/world/world_time_service.gd index 378fcd1..c3186b4 100644 --- a/world/world_time_service.gd +++ b/world/world_time_service.gd @@ -3,6 +3,8 @@ extends Node signal time_changed(time_hours: float, phase: Phase) signal phase_changed(phase: Phase) +signal authoritative_time_set(time_hours: float) +signal natural_time_advanced(hours: float) enum Phase { DAWN, @@ -28,6 +30,7 @@ var _persistent_time_hours: float = DEFAULT_START_HOUR var _persistence_tracking_enabled: bool = false var _running: bool = false var _phase: Phase = Phase.DAWN +var _last_emitted_clock_minute: int = floori(DEFAULT_START_HOUR * 60.0) func _ready() -> void: @@ -53,10 +56,12 @@ func end_session() -> void: func advance_time(real_seconds: float) -> void: if not _running or real_seconds <= 0.0: return + var advanced_hours := real_seconds * HOURS_PER_REAL_SECOND _set_time_hours( - _time_hours + real_seconds * HOURS_PER_REAL_SECOND, + _time_hours + advanced_hours, false, ) + natural_time_advanced.emit(advanced_hours) func synchronize_time(authoritative_time_hours: float) -> void: @@ -65,6 +70,18 @@ func synchronize_time(authoritative_time_hours: float) -> void: _set_time_hours(authoritative_time_hours, true) +func set_authoritative_time(time_hours: float) -> bool: + if ( + not is_finite(time_hours) + or time_hours < 0.0 + or time_hours >= HOURS_PER_DAY + ): + return false + _set_time_hours(time_hours, true) + authoritative_time_set.emit(_time_hours) + return true + + func set_persistence_tracking_enabled(enabled: bool) -> void: if _persistence_tracking_enabled == enabled: return @@ -140,14 +157,18 @@ func _set_time_hours(time_hours: float, force_emit: bool) -> void: var normalized: float = _normalized_hour(time_hours) var next_phase: Phase = phase_for_hour(normalized) var phase_was_changed: bool = next_phase != _phase - var time_was_changed: bool = not is_equal_approx(normalized, _time_hours) + var clock_minute: int = floori(normalized * 60.0) + var clock_minute_changed: bool = ( + clock_minute != _last_emitted_clock_minute + ) _time_hours = normalized _phase = next_phase if _persistence_tracking_enabled: _persistent_time_hours = normalized if phase_was_changed: phase_changed.emit(_phase) - if force_emit or time_was_changed or phase_was_changed: + if force_emit or clock_minute_changed or phase_was_changed: + _last_emitted_clock_minute = clock_minute time_changed.emit(_time_hours, _phase) diff --git a/world/world_time_visual_controller.gd b/world/world_time_visual_controller.gd index 611e60b..99b0429 100644 --- a/world/world_time_visual_controller.gd +++ b/world/world_time_visual_controller.gd @@ -5,6 +5,35 @@ const UPDATE_INTERVAL_SECONDS: float = 0.1 const SUN_YAW_DEGREES: float = -32.0 const WEATHER_TRANSITION_SECONDS: float = 10.0 const RAIN_EMITTER_OFFSET := Vector3(0.0, 7.0, 0.0) +const RAIN_EMISSION_EXTENTS := Vector3(13.0, 1.0, 13.0) +const RAIN_VISIBILITY_AABB := AABB( + Vector3(-13.5, -9.0, -13.5), + Vector3(27.0, 12.0, 27.0), +) +const RAIN_PARTICLE_AMOUNT: int = 2240 +const RAIN_VELOCITY_MIN: float = 16.0 +const RAIN_VELOCITY_MAX: float = 20.0 +const RAIN_DROP_SIZE := Vector3(0.014, 0.34, 0.014) +const FOG_DAYLIGHT_SCENE_BRIGHTNESS: float = 0.42 +const FOG_DAYLIGHT_FOG_LIGHT_BRIGHTNESS: float = 0.42 +const FOG_DAYLIGHT_WATER_BRIGHTNESS: float = 0.42 +const FOG_DAYLIGHT_POST_BRIGHTNESS: float = 0.50 +const RAIN_DAYLIGHT_POST_BRIGHTNESS: float = 0.52 +const DEFAULT_SURFACE_HIGHLIGHT_FLOOR: float = 0.72 +const FOGGY_NIGHT_SURFACE_HIGHLIGHT_FLOOR: float = 0.25 +const FOGGY_NIGHT_WATER_NEAR_FOG_AMOUNT: float = 1.0 +const FOGGY_DARK_WATER_FOG_CORRECTION := Color(1.15, 1.025, 1.0, 1.0) +const FOGGY_BRIGHT_WATER_FOG_CORRECTION := Color(0.99, 1.005, 1.01, 1.0) +const RAIN_WATER_FOG_COLOR := Color(0.025, 0.038, 0.045) +const SALT_WATER_MATERIAL: ShaderMaterial = preload( + "res://world/materials/stylized_water.tres" +) +const FRESH_WATER_MATERIAL: ShaderMaterial = preload( + "res://world/materials/stylized_water_fresh.tres" +) +const LocalStormCloudLayerType = preload( + "res://world/environment/local_storm_cloud_layer.gd" +) const DAY_SKY_TOP := Color(0.204, 0.498, 0.643) const DAY_SKY_HORIZON := Color(0.663, 0.843, 0.847) @@ -22,6 +51,7 @@ const NIGHT_GROUND_HORIZON := Color(0.075, 0.135, 0.22) const NIGHT_AMBIENT := Color(0.28, 0.35, 0.48) const NIGHT_FOG := Color(0.13, 0.21, 0.32) const NIGHT_SUN := Color(0.35, 0.44, 0.62) +const NIGHT_WATER_TINT := Color(0.34, 0.48, 0.58) const WARM_SKY_TOP := Color(0.31, 0.30, 0.42) const WARM_SKY_HORIZON := Color(0.96, 0.48, 0.22) @@ -30,21 +60,30 @@ const WARM_GROUND_HORIZON := Color(0.82, 0.34, 0.18) const WARM_AMBIENT := Color(0.92, 0.58, 0.40) const WARM_FOG := Color(0.74, 0.39, 0.27) const WARM_SUN := Color(1.0, 0.58, 0.30) +const WARM_WATER_TINT := Color(0.78, 0.62, 0.58) const WARM_SUN_DISC := Color(1.0, 0.45, 0.16) const MOON_DISC := Color(0.78, 0.88, 1.0) - -const CLOUDY_TINT := Color(0.53, 0.61, 0.66) -const RAINY_TINT := Color(0.31, 0.42, 0.50) -const FOGGY_TINT := Color(0.62, 0.70, 0.72) +const DAY_CLOUD_LIGHT := Color(0.70, 0.76, 0.77) +const DAY_CLOUD_SHADOW := Color(0.31, 0.38, 0.40) +const NIGHT_CLOUD_LIGHT := Color(0.18, 0.22, 0.30) +const NIGHT_CLOUD_SHADOW := Color(0.06, 0.09, 0.14) +const WARM_CLOUD_LIGHT := Color(0.76, 0.55, 0.45) +const WARM_CLOUD_SHADOW := Color(0.31, 0.23, 0.24) +const STORM_CLOUD_SHADOW := Color(0.08, 0.12, 0.14) +const DAY_LOWER_CLOUD := Color(0.22, 0.25, 0.26) +const NIGHT_LOWER_CLOUD := Color(0.08, 0.10, 0.14) +const WARM_LOWER_CLOUD := Color(0.22, 0.16, 0.16) var _time_service: WorldTimeService var _weather_service: WorldWeatherService var _world_environment: WorldEnvironment var _sun: DirectionalLight3D var _rain_target: Node3D +var _rain_camera_provider: Callable var _environment: Environment var _sky_material: ShaderMaterial var _rain: GPUParticles3D +var _storm_clouds: LocalStormCloudLayer var _elapsed: float = 0.0 var _weather_from: WorldWeatherService.Weather = ( WorldWeatherService.Weather.SUNNY @@ -61,16 +100,19 @@ func setup( sun: DirectionalLight3D, weather_service: WorldWeatherService = null, rain_target: Node3D = null, + rain_camera_provider: Callable = Callable(), ) -> void: _time_service = time_service _weather_service = weather_service _world_environment = world_environment _sun = sun _rain_target = rain_target + _rain_camera_provider = rain_camera_provider if not _prepare_runtime_environment(): set_process(false) return _prepare_rain() + _prepare_storm_clouds() if _weather_service != null: _weather_from = _weather_service.get_weather() _weather_to = _weather_from @@ -145,27 +187,25 @@ func _prepare_runtime_environment() -> bool: func _prepare_rain() -> void: _rain = GPUParticles3D.new() _rain.name = "LocalRain" - _rain.amount = 480 + _rain.amount = RAIN_PARTICLE_AMOUNT _rain.amount_ratio = 0.0 _rain.lifetime = 1.25 _rain.fixed_fps = 30 _rain.local_coords = false - _rain.visibility_aabb = AABB( - Vector3(-7.0, -9.0, -7.0), Vector3(14.0, 12.0, 14.0) - ) + _rain.visibility_aabb = RAIN_VISIBILITY_AABB var process_material := ParticleProcessMaterial.new() process_material.emission_shape = ( ParticleProcessMaterial.EMISSION_SHAPE_BOX ) - process_material.emission_box_extents = Vector3(6.5, 1.0, 6.5) + process_material.emission_box_extents = RAIN_EMISSION_EXTENTS process_material.direction = Vector3.DOWN process_material.spread = 5.0 - process_material.initial_velocity_min = 11.0 - process_material.initial_velocity_max = 15.0 + process_material.initial_velocity_min = RAIN_VELOCITY_MIN + process_material.initial_velocity_max = RAIN_VELOCITY_MAX process_material.gravity = Vector3(0.0, -2.0, 0.0) _rain.process_material = process_material var drop_mesh := BoxMesh.new() - drop_mesh.size = Vector3(0.018, 0.42, 0.018) + drop_mesh.size = RAIN_DROP_SIZE var drop_material := StandardMaterial3D.new() drop_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA drop_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED @@ -176,6 +216,13 @@ func _prepare_rain() -> void: _update_rain_position() +func _prepare_storm_clouds() -> void: + _storm_clouds = LocalStormCloudLayerType.new() + _storm_clouds.name = "LocalStormClouds" + add_child(_storm_clouds) + _storm_clouds.setup(_rain_target) + + func _apply_time(time_hours: float) -> void: if _environment == null or _sky_material == null or _sun == null: return @@ -212,64 +259,104 @@ func _apply_time(time_hours: float) -> void: var fog_color: Color = _blended_color( NIGHT_FOG, DAY_FOG, WARM_FOG, daylight, warmth ) - var weather_tint: Color = _weather_color() - var tint_strength: float = _weather_value( - 0.0, 0.30, 0.48, 0.62 + var foggy_fog_amount: float = _weather_value(0.0, 0.0, 0.0, 1.0) + var rain_fog_amount: float = _weather_value(0.0, 0.0, 1.0, 0.0) + var fog_daylight_amount: float = daylight * foggy_fog_amount + var rain_daylight_amount: float = daylight * rain_fog_amount + var weather_fog_amount: float = minf( + rain_fog_amount + foggy_fog_amount, + 1.0, + ) + var foggy_horizon_occlusion := _foggy_horizon_occlusion_amount(hour) + _environment.fog_enabled = weather_fog_amount > 0.001 + var fog_scene_brightness: float = lerpf( + 1.0, + FOG_DAYLIGHT_SCENE_BRIGHTNESS, + fog_daylight_amount, ) _sky_material.set_shader_parameter( - "sky_top_color", sky_top.lerp(weather_tint, tint_strength) + "sky_top_color", sky_top ) _sky_material.set_shader_parameter( - "sky_horizon_color", sky_horizon.lerp(weather_tint, tint_strength) + "sky_horizon_color", + sky_horizon, ) _sky_material.set_shader_parameter( "ground_bottom_color", - ground_bottom.lerp(weather_tint, tint_strength * 0.62), + ground_bottom, ) _sky_material.set_shader_parameter( "ground_horizon_color", - ground_horizon.lerp(weather_tint, tint_strength * 0.76), + ground_horizon, ) + _sky_material.set_shader_parameter("fog_horizon_color", fog_color) + _sky_material.set_shader_parameter( + "fog_horizon_occlusion", + foggy_fog_amount * foggy_horizon_occlusion, + ) + _apply_sky_clouds(daylight, warmth) _environment.background_energy_multiplier = ( lerpf(0.52, 0.85, daylight) - * _weather_value(1.0, 0.82, 0.66, 0.74) - ) - _environment.ambient_light_color = ambient.lerp( - weather_tint, tint_strength * 0.45 + * _weather_value(1.0, 0.82, 0.66, 1.0) + * fog_scene_brightness ) + _environment.ambient_light_color = ambient _environment.ambient_light_energy = ( lerpf(0.66, 1.08, daylight) - * _weather_value(1.0, 0.88, 0.76, 0.82) + * _weather_value(1.0, 0.88, 0.76, 1.0) + * fog_scene_brightness ) - _environment.fog_light_color = fog_color.lerp( - weather_tint, tint_strength * 0.82 - ) - _environment.fog_light_energy = ( + _environment.fog_light_color = fog_color + var fog_light_energy := ( lerpf(0.56, 0.82, daylight) - * _weather_value(1.0, 0.92, 0.82, 1.05) + * _weather_value(1.0, 0.92, 0.82, 1.0) + * lerpf( + 1.0, + FOG_DAYLIGHT_FOG_LIGHT_BRIGHTNESS, + fog_daylight_amount, + ) ) + _environment.fog_light_energy = fog_light_energy _environment.fog_aerial_perspective = _weather_value( - 0.35, 0.48, 0.62, 0.92 + 0.35, 0.48, 0.62, 0.0 ) _environment.fog_sky_affect = _weather_value( - 0.35, 0.48, 0.68, 0.94 + 0.35, 0.48, 0.68, 1.0 ) _environment.fog_depth_curve = _weather_value( 1.6, 1.5, 1.4, 1.18 ) _environment.fog_depth_begin = _weather_value( - 42.0, 32.0, 20.0, 4.0 + 42.0, 32.0, 20.0, 3.0 ) _environment.fog_depth_end = _weather_value( - 170.0, 140.0, 95.0, 42.0 + 170.0, 140.0, 95.0, 18.0 + ) + _apply_water_environment( + daylight, + warmth, + rain_fog_amount, + foggy_fog_amount, + foggy_horizon_occlusion, + _fog_render_color(fog_color, fog_light_energy), ) _environment.adjustment_brightness = ( lerpf(0.93, 0.98, daylight) - * _weather_value(1.0, 0.97, 0.92, 0.96) + * _weather_value(1.0, 0.97, 0.92, 1.0) + * lerpf( + 1.0, + FOG_DAYLIGHT_POST_BRIGHTNESS, + fog_daylight_amount, + ) + * lerpf( + 1.0, + RAIN_DAYLIGHT_POST_BRIGHTNESS, + rain_daylight_amount, + ) ) _environment.adjustment_saturation = ( lerpf(0.82, 0.91, daylight) - * _weather_value(1.0, 0.88, 0.78, 0.70) + * _weather_value(1.0, 0.88, 0.78, 1.0) ) _sun.rotation_degrees = Vector3( -360.0 * fposmod(hour - WorldTimeService.DAY_START_HOUR, 24.0) / 24.0, @@ -284,7 +371,7 @@ func _apply_time(time_hours: float) -> void: ) _sky_material.set_shader_parameter( "sun_visibility", - daylight * _weather_value(1.0, 0.65, 0.28, 0.15), + daylight * _weather_value(1.0, 0.65, 0.28, 1.0), ) _sky_material.set_shader_parameter( "moon_direction", -_sun.global_transform.basis.z.normalized() @@ -292,18 +379,205 @@ func _apply_time(time_hours: float) -> void: _sky_material.set_shader_parameter("moon_color", MOON_DISC) _sky_material.set_shader_parameter( "moon_visibility", - (1.0 - daylight) * _weather_value(1.0, 0.72, 0.36, 0.20), + (1.0 - daylight) * _weather_value(1.0, 0.72, 0.36, 1.0), ) _sun.light_color = _blended_color( NIGHT_SUN, DAY_SUN, WARM_SUN, daylight, warmth ) _sun.light_energy = ( lerpf(0.0, 0.10, daylight) - * _weather_value(1.0, 0.55, 0.25, 0.35) + * _weather_value(1.0, 0.55, 0.25, 0.30) ) _update_rain_amount() +func _apply_water_environment( + daylight: float, + warmth: float, + rain_fog_amount: float, + foggy_fog_amount: float, + foggy_horizon_occlusion: float, + fog_render_color: Color, +) -> void: + var water_tint := _blended_color( + NIGHT_WATER_TINT, + Color.WHITE, + WARM_WATER_TINT, + daylight, + warmth, + ) + var water_brightness := ( + lerpf(0.34, 1.0, daylight) + * _weather_value(1.0, 0.90, 0.78, 1.0) + * lerpf( + 1.0, + FOG_DAYLIGHT_WATER_BRIGHTNESS, + daylight * _weather_value(0.0, 0.0, 0.0, 1.0), + ) + ) + var weather_fog_amount: float = minf( + rain_fog_amount + foggy_fog_amount, + 1.0, + ) + var foggy_color_weight: float = ( + foggy_fog_amount / weather_fog_amount + if weather_fog_amount > 0.001 + else 0.0 + ) + var water_fog_color := RAIN_WATER_FOG_COLOR.lerp( + fog_render_color, + foggy_color_weight, + ) + var fog_correction_amount := ( + foggy_fog_amount * foggy_horizon_occlusion + ) + var bright_fog_amount := maxf(daylight, warmth) + var fog_color_correction := FOGGY_DARK_WATER_FOG_CORRECTION.lerp( + FOGGY_BRIGHT_WATER_FOG_CORRECTION, + bright_fog_amount, + ) + water_fog_color.r *= lerpf( + 1.0, + fog_color_correction.r, + fog_correction_amount, + ) + water_fog_color.g *= lerpf( + 1.0, + fog_color_correction.g, + fog_correction_amount, + ) + water_fog_color.b *= lerpf( + 1.0, + fog_color_correction.b, + fog_correction_amount, + ) + var foggy_surface_highlight_floor := lerpf( + FOGGY_NIGHT_SURFACE_HIGHLIGHT_FLOOR, + DEFAULT_SURFACE_HIGHLIGHT_FLOOR, + daylight, + ) + var surface_highlight_floor := lerpf( + DEFAULT_SURFACE_HIGHLIGHT_FLOOR, + foggy_surface_highlight_floor, + foggy_fog_amount, + ) + var water_near_fog_amount := ( + FOGGY_NIGHT_WATER_NEAR_FOG_AMOUNT + * foggy_fog_amount + * foggy_horizon_occlusion + ) + for material: ShaderMaterial in [ + SALT_WATER_MATERIAL, + FRESH_WATER_MATERIAL, + ]: + material.set_shader_parameter("environment_tint", water_tint) + material.set_shader_parameter( + "environment_brightness", + water_brightness, + ) + material.set_shader_parameter("weather_fog_color", water_fog_color) + material.set_shader_parameter( + "surface_highlight_night_floor", + surface_highlight_floor, + ) + material.set_shader_parameter( + "weather_fog_amount", + weather_fog_amount, + ) + material.set_shader_parameter( + "weather_fog_near_amount", + water_near_fog_amount, + ) + material.set_shader_parameter( + "weather_fog_begin", + _weather_value(42.0, 32.0, 20.0, 3.0), + ) + material.set_shader_parameter( + "weather_fog_end", + _weather_value(170.0, 140.0, 95.0, 18.0), + ) + material.set_shader_parameter( + "weather_fog_curve", + _weather_value(1.6, 1.5, 1.4, 1.18), + ) + + +func _fog_render_color(fog_color: Color, fog_light_energy: float) -> Color: + var linear_fog_color := fog_color.srgb_to_linear() + linear_fog_color.r *= fog_light_energy + linear_fog_color.g *= fog_light_energy + linear_fog_color.b *= fog_light_energy + linear_fog_color.a = 1.0 + return linear_fog_color.linear_to_srgb() + + +func _foggy_horizon_occlusion_amount(hour: float) -> float: + # Day and dusk already blend cleanly. During night and most of dawn, make + # the manually fogged transparent water fully occluding so the dark scene + # below it cannot reintroduce a horizon band. Brief edge fades avoid a pop + # where the dawn/night phases meet their neighboring phases. + if hour >= WorldTimeService.DUSK_END_HOUR - 0.1: + return smoothstep( + WorldTimeService.DUSK_END_HOUR - 0.1, + WorldTimeService.DUSK_END_HOUR, + hour, + ) + if hour < WorldTimeService.DAWN_START_HOUR: + return 1.0 + if hour < WorldTimeService.DAWN_END_HOUR: + return 1.0 - smoothstep( + WorldTimeService.DAWN_END_HOUR - 0.1, + WorldTimeService.DAWN_END_HOUR, + hour, + ) + return 0.0 + + +func _apply_sky_clouds(daylight: float, warmth: float) -> void: + var cloud_light := _blended_color( + NIGHT_CLOUD_LIGHT, + DAY_CLOUD_LIGHT, + WARM_CLOUD_LIGHT, + daylight, + warmth, + ) + var cloud_shadow := _blended_color( + NIGHT_CLOUD_SHADOW, + DAY_CLOUD_SHADOW, + WARM_CLOUD_SHADOW, + daylight, + warmth, + ) + var storm_amount: float = _weather_value(0.0, 0.18, 0.55, 0.0) + cloud_light = cloud_light.lerp(cloud_shadow, storm_amount * 0.35) + cloud_shadow = cloud_shadow.lerp( + STORM_CLOUD_SHADOW, + storm_amount * 0.10, + ) + var lower_cloud_color := _blended_color( + NIGHT_LOWER_CLOUD, + DAY_LOWER_CLOUD, + WARM_LOWER_CLOUD, + daylight, + warmth, + ) + _sky_material.set_shader_parameter( + "cloud_coverage", + _weather_value(0.0, 0.58, 0.985, 0.0), + ) + _sky_material.set_shader_parameter( + "cloud_opacity", + _weather_value(0.0, 0.76, 1.0, 0.0), + ) + _sky_material.set_shader_parameter("cloud_light_color", cloud_light) + _sky_material.set_shader_parameter("cloud_shadow_color", cloud_shadow) + if _storm_clouds != null: + _storm_clouds.set_storm_amount( + _weather_value(0.0, 0.0, 1.0, 0.0), + lower_cloud_color, + ) + + func _on_weather_changed( weather: WorldWeatherService.Weather, _seconds_remaining: float, @@ -345,25 +619,6 @@ func _weather_state_value( return sunny -func _weather_color() -> Color: - return _weather_state_color(_weather_from).lerp( - _weather_state_color(_weather_to), _weather_transition - ) - - -func _weather_state_color( - weather: WorldWeatherService.Weather, -) -> Color: - match weather: - WorldWeatherService.Weather.CLOUDY: - return CLOUDY_TINT - WorldWeatherService.Weather.RAINY: - return RAINY_TINT - WorldWeatherService.Weather.FOGGY: - return FOGGY_TINT - return Color.WHITE - - func _update_rain_amount() -> void: if _rain == null: return @@ -375,7 +630,14 @@ func _update_rain_amount() -> void: func _update_rain_position() -> void: if _rain == null or _rain_target == null: return - _rain.global_position = _rain_target.global_position + RAIN_EMITTER_OFFSET + var anchor_position: Vector3 = _rain_target.global_position + if _rain_camera_provider.is_valid(): + var camera_candidate: Variant = _rain_camera_provider.call() + if camera_candidate is Camera3D: + var active_camera := camera_candidate as Camera3D + if is_instance_valid(active_camera): + anchor_position = active_camera.global_position + _rain.global_position = anchor_position + RAIN_EMITTER_OFFSET func _daylight_amount(hour: float) -> float: diff --git a/world/world_weather_service.gd b/world/world_weather_service.gd index d01728c..4fce78e 100644 --- a/world/world_weather_service.gd +++ b/world/world_weather_service.gd @@ -15,11 +15,23 @@ const SUNNY_DURATION_RANGE := Vector2(480.0, 900.0) const CLOUDY_DURATION_RANGE := Vector2(300.0, 720.0) const RAINY_DURATION_RANGE := Vector2(300.0, 600.0) const FOGGY_DURATION_RANGE := Vector2(300.0, 600.0) +const MAX_PERSISTED_SECONDS: float = 1800.0 +const DAILY_PLAN_SEGMENT_HOURS: float = 2.0 +const DAILY_PLAN_SEGMENT_COUNT: int = 12 var _weather: Weather = DEFAULT_WEATHER var _seconds_remaining: float = SUNNY_DURATION_RANGE.x +var _persistent_weather: Weather = DEFAULT_WEATHER +var _persistent_seconds_remaining: float = SUNNY_DURATION_RANGE.x +var _has_persistent_state: bool = false +var _persistence_tracking_enabled: bool = false var _running_authority: bool = false var _rng := RandomNumberGenerator.new() +var _daily_plan_id: String = "" +var _daily_schedule: Array[Dictionary] = [] +var _world_time: WorldTimeService +var _manual_override_weather: Weather = DEFAULT_WEATHER +var _manual_override_segment_index: int = -1 func _ready() -> void: @@ -34,30 +46,52 @@ func begin_authoritative_session(seed_value: int) -> void: _rng.seed = seed_value _running_authority = true set_process(true) - _set_weather(DEFAULT_WEATHER, _roll_duration(DEFAULT_WEATHER), true) + _clear_manual_override() + if _daily_schedule.is_empty() or _world_time == null: + if _has_persistent_state: + _set_weather( + _persistent_weather, + _persistent_seconds_remaining, + true, + ) + else: + _set_weather( + DEFAULT_WEATHER, + _roll_duration(DEFAULT_WEATHER), + true, + ) + else: + _update_scheduled_weather(true) func begin_remote_session() -> void: _running_authority = false set_process(false) + _clear_manual_override() _set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true) func end_session() -> void: _running_authority = false set_process(false) + _clear_manual_override() _set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true) func advance_weather(real_seconds: float) -> void: if not _running_authority or real_seconds <= 0.0: return + if not _daily_schedule.is_empty() and _world_time != null: + _update_scheduled_weather(false) + return _seconds_remaining -= real_seconds while _seconds_remaining <= 0.0: var overrun: float = -_seconds_remaining var next_weather: Weather = _choose_next_weather() _set_weather(next_weather, _roll_duration(next_weather), true) _seconds_remaining -= overrun + if _persistence_tracking_enabled: + _store_persistent_state() func apply_authoritative_snapshot( @@ -69,6 +103,72 @@ func apply_authoritative_snapshot( _set_weather(weather, maxf(seconds_remaining, 0.0), false) +func set_authoritative_weather(weather: Weather) -> bool: + if not _running_authority or not is_valid_weather(int(weather)): + return false + var duration: float = _roll_duration(weather) + if not _daily_schedule.is_empty() and _world_time != null: + _manual_override_weather = weather + _manual_override_segment_index = _current_daily_segment_index() + duration = _current_daily_segment_seconds_remaining( + _manual_override_segment_index + ) + else: + _clear_manual_override() + _set_weather(weather, duration, true) + return true + + +func set_persistence_tracking_enabled(enabled: bool) -> void: + if _persistence_tracking_enabled == enabled: + return + if _persistence_tracking_enabled: + _store_persistent_state() + _persistence_tracking_enabled = enabled + + +func restore_persistent_state( + weather: Weather, + seconds_remaining: float, +) -> bool: + if not is_valid_persistent_state(weather, seconds_remaining): + return false + _persistent_weather = weather + _persistent_seconds_remaining = seconds_remaining + _has_persistent_state = true + if _persistence_tracking_enabled: + _set_weather( + _persistent_weather, + _persistent_seconds_remaining, + true, + ) + return true + + +func reset_persistent_state() -> void: + _has_persistent_state = false + _persistent_weather = DEFAULT_WEATHER + _persistent_seconds_remaining = SUNNY_DURATION_RANGE.x + if _persistence_tracking_enabled and _running_authority: + _set_weather( + DEFAULT_WEATHER, + _roll_duration(DEFAULT_WEATHER), + true, + ) + + +func has_persistent_state() -> bool: + return _has_persistent_state + + +func get_persistent_weather() -> Weather: + return _persistent_weather + + +func get_persistent_seconds_remaining() -> float: + return _persistent_seconds_remaining + + func get_weather() -> Weather: return _weather @@ -89,10 +189,97 @@ func get_weather_name() -> String: return weather_name(_weather) +func configure_daily_plan( + plan_id: String, + schedule: Array, + world_time: WorldTimeService, +) -> bool: + if ( + plan_id.is_empty() + or not is_valid_daily_plan_schedule(schedule) + or world_time == null + ): + return false + _daily_plan_id = plan_id + _daily_schedule.clear() + _clear_manual_override() + for value: Variant in schedule: + _daily_schedule.append((value as Dictionary).duplicate(true)) + _world_time = world_time + if _running_authority: + _update_scheduled_weather(true) + return true + + +func clear_daily_plan() -> void: + _daily_plan_id = "" + _daily_schedule.clear() + _world_time = null + _clear_manual_override() + + +func get_daily_plan_id() -> String: + return _daily_plan_id + + static func is_valid_weather(value: int) -> bool: return value >= Weather.SUNNY and value <= Weather.FOGGY +static func is_valid_persistent_state( + weather: Weather, + seconds_remaining: float, +) -> bool: + return ( + is_valid_weather(int(weather)) + and is_finite(seconds_remaining) + and seconds_remaining >= 0.0 + and seconds_remaining <= MAX_PERSISTED_SECONDS + ) + + +static func is_valid_daily_plan_schedule(value: Variant) -> bool: + if typeof(value) != TYPE_ARRAY: + return false + var schedule: Array = value + if schedule.size() != DAILY_PLAN_SEGMENT_COUNT: + return false + for entry_value: Variant in schedule: + if typeof(entry_value) != TYPE_DICTIONARY: + return false + var entry: Dictionary = entry_value + if ( + typeof(entry.get("start_hour")) not in [TYPE_FLOAT, TYPE_INT] + or not is_finite(float(entry.get("start_hour", -1.0))) + or float(entry.get("start_hour", -1.0)) < 0.0 + or float(entry.get("start_hour", -1.0)) >= 24.0 + or not _is_bounded_integer( + entry.get("weather"), Weather.SUNNY, Weather.FOGGY + ) + or not is_valid_weather(int(entry.get("weather", -1))) + ): + return false + return true + + +static func _is_bounded_integer( + value: Variant, + minimum: int, + maximum: int, +) -> bool: + if typeof(value) == TYPE_INT: + return int(value) >= minimum and int(value) <= maximum + if typeof(value) != TYPE_FLOAT: + return false + var number: float = float(value) + return ( + is_finite(number) + and number >= float(minimum) + and number <= float(maximum) + and is_equal_approx(number, round(number)) + ) + + static func weather_name(weather: Weather) -> String: match weather: Weather.SUNNY: @@ -114,10 +301,18 @@ func _set_weather( var changed: bool = weather != _weather _weather = weather _seconds_remaining = maxf(seconds_remaining, 0.0) + if _persistence_tracking_enabled: + _store_persistent_state() if force_emit or changed: weather_changed.emit(_weather, _seconds_remaining) +func _store_persistent_state() -> void: + _persistent_weather = _weather + _persistent_seconds_remaining = _seconds_remaining + _has_persistent_state = true + + func _choose_next_weather() -> Weather: match _weather: Weather.SUNNY: @@ -150,3 +345,48 @@ func _duration_range(weather: Weather) -> Vector2: Weather.FOGGY: return FOGGY_DURATION_RANGE return SUNNY_DURATION_RANGE + + +func _update_scheduled_weather(force_emit: bool) -> void: + if _daily_schedule.is_empty() or _world_time == null: + return + var segment_index: int = _current_daily_segment_index() + var entry: Dictionary = _daily_schedule[segment_index] + var weather: Weather = int(entry.get("weather", DEFAULT_WEATHER)) as Weather + if _manual_override_segment_index == segment_index: + weather = _manual_override_weather + elif _manual_override_segment_index >= 0: + _clear_manual_override() + var seconds_remaining: float = _current_daily_segment_seconds_remaining( + segment_index + ) + _set_weather(weather, seconds_remaining, force_emit) + + +func _current_daily_segment_index() -> int: + var elapsed_hours: float = fposmod( + _world_time.get_time_hours() - WorldTimeService.DAY_START_HOUR, + WorldTimeService.HOURS_PER_DAY, + ) + return clampi( + floori(elapsed_hours / DAILY_PLAN_SEGMENT_HOURS), + 0, + _daily_schedule.size() - 1, + ) + + +func _current_daily_segment_seconds_remaining(segment_index: int) -> float: + var elapsed_hours: float = fposmod( + _world_time.get_time_hours() - WorldTimeService.DAY_START_HOUR, + WorldTimeService.HOURS_PER_DAY, + ) + var next_boundary: float = ( + float(segment_index + 1) * DAILY_PLAN_SEGMENT_HOURS + ) + var hours_remaining: float = maxf(next_boundary - elapsed_hours, 0.0) + return hours_remaining / WorldTimeService.HOURS_PER_REAL_SECOND + + +func _clear_manual_override() -> void: + _manual_override_weather = DEFAULT_WEATHER + _manual_override_segment_index = -1